General Setup

Setup chunk

Setup reticulate

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/liver_regen"

Load libraries

library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] TRUE
use_python(Sys.which("python"))
py_config()
python:         /home/tpires/bin/miniconda3/bin/python
libpython:      /home/tpires/bin/miniconda3/lib/libpython3.8.so
pythonhome:     /home/tpires/bin/miniconda3:/home/tpires/bin/miniconda3
version:        3.8.3 (default, May 19 2020, 18:47:26)  [GCC 7.3.0]
numpy:          /home/tpires/bin/miniconda3/lib/python3.8/site-packages/numpy
numpy_version:  1.18.5

NOTE: Python version was forced by RETICULATE_PYTHON

Load and preprocess data

Load data (from all cells)

library(Seurat)
library(ggplot2)
library(ggridges)
library(dplyr)
library(igraph)
library(data.table)

Prepare a global object with all necessary metadata

allcells_css = readRDS(file = "data/processed/allcells_css.RDS")
end_cells = readRDS(file = "results/endothelial/only_end_cells_zon.RDS")
hep_cells = readRDS(file = "results/zonation_cond/hep_cells_zonation_rank.RDS")
imm_cells = readRDS(file = "results/immune/all_imm_cells.RDS")

Subset and process each condition

endpop_df = data.frame(row.names = colnames(end_cells),
                       "subpops" = end_cells@meta.data$endo_simp)
immpop_df = data.frame(row.names = colnames(imm_cells),
                       "subpops" = imm_cells@meta.data$immune_annot)
heppop_df = lapply(hep_cells, function(x) cbind(colnames(x), as.character(x$zonation_int)))
heppop_df = Reduce(rbind, heppop_df)
heppop_df = data.frame(row.names = heppop_df[,1],
                       subpops = factor(heppop_df[,2]))
levels(heppop_df$subpops) = c("(-0.00099,0.333]" = "Hepatocytes_Z1",
                              "(0.333,0.667]" = "Hepatocytes_Z2",
                              "(0.667,1]" = "Hepatocytes_Z3")
heppop_df$subpops = as.character(heppop_df$subpops)

subpop_df = rbind(endpop_df, immpop_df, heppop_df)
subpop_df$subpops[subpop_df$subpops=="Cycling cells"] = "Dividing endothelial cells"

# add subpop metadata
allcells_css = AddMetaData(allcells_css, subpop_df)
allcells_css$subpops[is.na(allcells_css$subpops)] = allcells_css$allcells_major[is.na(allcells_css$subpops)]
allcells_css$subpops[allcells_css$subpops=="Hepatocyte-Monocyte interaction"] = "Doublets"

# the cells in this object named "Hepatocytes", "Endothelial cells", and "Doublets" have to be removed
## the first two are cells that didn't pass the hep and end analysis
sub_allcells_css = allcells_css[,!(allcells_css$subpops %in% c("Hepatocytes", "Endothelial cells",
                                                               "Doublets"))]

# add simplified column - merge some populations, don't include cycling pops and stressed T cells
sub_allcells_css$subpops_simp = sub_allcells_css$subpops
sub_allcells_css$subpops_simp[grepl("MAIT", sub_allcells_css$subpops_simp)] = "MAIT cells"
sub_allcells_css$subpops_simp[grepl("NK cells ", sub_allcells_css$subpops_simp)] = "NK cells"
sub_allcells_css$subpops_simp[grepl("LSEC (high MT", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "LSEC (high MT)"
sub_allcells_css$subpops_simp[grepl("CD8 ab-T ", sub_allcells_css$subpops_simp, 
                                    fixed = T)] = "CD8 ab-T cells"
simp_allcells_css = sub_allcells_css[,!(sub_allcells_css$subpops_simp %in% c("ab-T cells (stress)", "Dividing cDCs", "Dividing endothelial cells","Dividing T/NK cells"))]

Running CellPhoneDB

Commands for runnin CellPhoneDB will be written here. Results are output to the local1 disk to avoid I/O issues, but the output folders should then be copied to: results/cell_comm_healthy/CellPhoneDB_runs.
NOTE: for some reason I’m getting a SegFault when trying to run the heatmap_plot command. This was run on the Euler server instead.

#cpdb_path = "results/cell_comm/CellPhoneDB_runs_updt"
cpdb_path = "/local1/USERS/tomasgomes/CellPhoneDB_runs_updt"

cond_cells = list()
for(cond in unique(sub_allcells_css@meta.data$Condition)){
  # subset condition
  cond_cells[[cond]] = sub_allcells_css[,sub_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                    "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                    "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops),
                    stringsAsFactors = F)
  write.table(meta, file = paste0(cpdb_path, "/", cond, "/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
  
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
  
  
  # subset condition
  cond_cells[[cond]] = simp_allcells_css[,simp_allcells_css@meta.data$Condition==cond]
  
  # define metadata
  meta_simp = data.frame(row.names = rownames(cond_cells[[cond]]@meta.data),
                         "Cell" = rownames(cond_cells[[cond]]@meta.data), 
                         "cell_type" = as.character(cond_cells[[cond]]@meta.data$subpops_simp),
                         stringsAsFactors = F)
  write.table(meta_simp, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_meta_names.txt"), 
            sep = "\t", col.names = T, row.names = F, quote = F)
   
  # normalise and save
  cond_cells[[cond]] = suppressWarnings(SCTransform(cond_cells[[cond]], do.correct.umi = T, 
                                                    vars.to.regress=c("unique_name", "nCount_RNA"),
                                                    variable.features.rv.th = 1, seed.use = 1,
                                                    return.only.var.genes = F, verbose = F,
                                                    variable.features.n = NULL))
  
  dat = cbind(rownames(cond_cells[[cond]]@assays$SCT@data),
              Matrix::as.matrix(cond_cells[[cond]]@assays$SCT@data))
  colnames(dat)[1] = "Gene"
  write.table(dat, file = paste0(cpdb_path, "/", cond, "_simp/", cond, "_exp_norm.txt"), 
              sep = "\t", col.names = T, row.names = F, quote = F)
}
for(n in names(cond_cells)){
  comm1 = paste0("cellphonedb method statistical_analysis /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_exp_norm.txt --threads=12 --output-path=/local1/USERS/tomasgomes/liver/CellPhoneDB_runs_updt/", n, "_simp --project-name ", n, "_simp --counts-data gene_name")
  comm2 = paste0("cp -r /local1/USERS/tomasgomes/liver/CellPhoneDB_runs_updt/", n, "_simp/", n, "_simp results/cell_comm/CellPhoneDB_runs_updt/")
  comm3 = paste0("cp -r /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/")
  comm4 = paste0("cellphonedb plot heatmap_plot --pvalues-path ./results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/pvalues.txt --output-path ./results/cell_comm/CellPhoneDB_runs_updt/", n, "_simp/ --count-name counts_heat.pdf --count-network-name count_net.txt --interaction-count-name count_inter.txt /local1/USERS/tomasgomes/CellPhoneDB_runs_updt/", n, "_simp/", n, "_meta_names.txt")
  
  print(n)
  print(comm1)
  print(comm2)
  print(comm3)
  print(comm4)
  print(".")
}

Process results

Load results

cpdb_path = "results/cell_comm/CellPhoneDB_runs_updt"

sig_means_names_l = list()
dec_l = list()
net_names_l = list()
meta_l = list()
conds = unique(allcells_css@meta.data$Condition)
for(n in c(conds, paste0(conds, "_simp"))){
  ns = strsplit(n, "_")[[1]][1]
  sig_means_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/significant_means.txt"),
                                      header = T, sep = "\t", stringsAsFactors = F)
  dec_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/deconvoluted.txt"),
                          header = T, sep = "\t")
  colnames(dec_l[[n]]) = gsub(".", " ", colnames(dec_l[[n]]), fixed = T)
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="gd T cells"] = "gd-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Macrophages  HES4  "] = "Macrophages (HES4+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells"] = "CD8 ab-T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 1"] = "CD8 ab-T cells 1"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 2"] = "CD8 ab-T cells 2"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="CD8 ab T cells 3"] = "CD8 ab-T cells 3"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Naive CD4  T cells"] = "Naive CD4+ T cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="ab T cells  stress "] = "ab-T cells (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  secretory "] = "Monocytes (secretory)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  TREM2  CD9  "] = "Monocytes (TREM2+ CD9+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Monocytes  IGSF21  GPR34  "] = "Monocytes (IGSF21+ GPR34+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  stress "] = "LSEC (stress)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  remodelling "] = "LSEC (remodelling)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  interferon "] = "LSEC (interferon)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 2 "] = "LSEC (high MT 2)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT 1 "] = "LSEC (high MT 1)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  high MT "] = "LSEC (high MT)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="LSEC  fenestr  "] = "LSEC (fenestr.)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Kupffer cells  SUCNR1  "] = "Kupffer cells (SUCNR1+)"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgG  Plasma cells"] = "IgG+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="IgA  Plasma cells"] = "IgA+ Plasma cells"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="EC non LSEC"] = "EC non-LSEC"
  colnames(dec_l[[n]])[colnames(dec_l[[n]])=="Dividing T NK cells"] = "Dividing T/NK cells"
  net_names_l[[n]] = read.table(paste0(cpdb_path, "/", n, "/count_net.txt"),
                                header = T, sep = "\t", stringsAsFactors = F)
  meta_l[[n]] = if(!grepl("simp", n)){
    read.table(paste0(cpdb_path, "/", n, "/", n, "_meta_names.txt"),header = T,sep = "\t")
  } else{
    read.table(paste0(cpdb_path, "/", n, "/", ns, "_meta_names.txt"),header = T,sep = "\t")
  }
}
saveRDS(dec_l, file = "results/cell_comm/updt/deconvoluted_list.RDS")
saveRDS(net_names_l, file = "results/cell_comm/updt/count_net_list.RDS")

Reformat significant means matrix

reform_list = list()
for(n in names(meta_l)){
  simp_reform = reshape2::melt(sig_means_names_l[[n]][,c(1:4,7:9,13:ncol(sig_means_names_l[[n]]))])
  simp_reform = simp_reform[complete.cases(simp_reform),]
  
  lr1 = c()
  lr2 = c()
  for(i in 1:nrow(simp_reform)){
    if(grepl("complex", simp_reform$partner_a[i])){
      lr1 = c(lr1, substr(simp_reform$partner_a[i], 9,100))
    } else{
      lr1 = c(lr1, strsplit(simp_reform$interacting_pair[i], "_")[[1]][1])
    }
    if(grepl("complex", simp_reform$partner_b[i])){
      lr2 = c(lr2, substr(simp_reform$partner_b[i], 9,100))
    } else{
      spltlen = length(strsplit(simp_reform$interacting_pair[i], "_")[[1]])
      lr2 = c(lr2, strsplit(simp_reform$interacting_pair[i], "_")[[1]][spltlen])
    }
  }
  simp_reform$lr1 = lr1
  simp_reform$lr2 = lr2
  
  simp_reform$ct1 = NA
  simp_reform$ct2 = NA
  donest = rep(NA, length(simp_reform$ct1))
  doneen = rep(NA, length(simp_reform$ct2))
  for(ct in sort(unique(meta_l[[n]]$cell_type), decreasing = T)){
    ct_mod = gsub("-", " ", ct, fixed = T)
    ct_mod = gsub("+", " ", ct_mod, fixed = T)
    ct_mod = gsub("/", " ", ct_mod, fixed = T)
    ct_mod = gsub("(", " ", ct_mod, fixed = T)
    ct_mod = gsub(")", " ", ct_mod, fixed = T)
    ct_mod = gsub(".", " ", ct_mod, fixed = T)
    st = grepl(paste0("^",ct_mod, " "), gsub(".", " ", simp_reform$variable, fixed = T), fixed = F)
    en = grepl(paste0(" ",ct_mod,"$"), gsub(".", " ", simp_reform$variable, fixed = T), fixed = F)
    simp_reform$ct1[st & is.na(donest)] = ct
    simp_reform$ct2[en & is.na(doneen)] = ct
    donest[st] = T
    doneen[en] = T
  }
  #colnames(dec_l[[n]])[!(colnames(dec_l[[n]]) %in% unique(simp_reform$ct2))]
  
  # direction: receptors activate internal signal - that is the direction of the signalling
  ## if both are true, the "net signal" is 0
  simp_reform$dir = ifelse(simp_reform$receptor_a==simp_reform$receptor_b, 0, 
                           ifelse(simp_reform$receptor_a=="True" & 
                                    simp_reform$receptor_b=="False", -1,
                                  ifelse(simp_reform$receptor_a=="False" &
                                           simp_reform$receptor_b=="True", 1, NA)))
  
  simp_reform = simp_reform[,c("id_cp_interaction", "ct1", "ct2", "lr1", "lr2", 
                               "value", "dir")]
  
  for(i in 1:nrow(simp_reform)){
    if(simp_reform[i,"dir"]==-1){
      tmp = simp_reform[i,"ct2"]
      simp_reform[i,"ct2"] = simp_reform[i,"ct1"]
      simp_reform[i,"ct1"] = tmp
      
      tmp = simp_reform[i,"lr2"]
      simp_reform[i,"lr2"] = simp_reform[i,"lr1"]
      simp_reform[i,"lr1"] = tmp
      
      simp_reform[i,"dir"]=1
    }
  }
  
 reform_list[[n]] = simp_reform
}
saveRDS(reform_list, file = "results/cell_comm/updt/reform_list.RDS")

Plot interaction counts

  simp_reform = reshape2::melt(sig_means_names_l[[n]][,c(1:4,7:9,13:ncol(sig_means_names_l[[n]]))])
Using id_cp_interaction, interacting_pair, partner_a, partner_b, secreted, receptor_a, receptor_b as id variables
  simp_reform = simp_reform[complete.cases(simp_reform),]

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean

inter_counts_l = list()
for(n in names(net_names_l)){
  inter_counts = reshape2::dcast(data = net_names_l[[n]], 
                                 formula = SOURCE~TARGET, value.var = "count")
  rownames(inter_counts) = inter_counts[,1]
  inter_counts = inter_counts[,-1]
  
  pdf(paste0("results/cell_comm/updt/", n, "_number_of_interactions.pdf"), useDingbats = F, 
      height = 10, width = 10)
  pheatmap::pheatmap(inter_counts, clustering_method = "ward.D2", main = "All clusters")
  dev.off()
  inter_counts_l[[n]] = inter_counts
}

Making two cell type by interaction matrices: one has the ligand mean, another the receptor mean (here only directional)

scoring_mats = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                           "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats[[n]]$cor_mat_both = cor_mat_both_cl
}
saveRDS(scoring_mats, file = "results/cell_comm/updt/scoring_mats.RDS")

Count interactions of each type

scoring_mats_dir = list()
for(n in names(reform_list)){
  cl_reform = reform_list[[n]]
  # add reversed non-directed interactions, to consider them in both directions
  cl_reform0 = cl_reform[cl_reform$dir==0,]
  cl_reform0 = cl_reform0[,c(1,3,2,5,4,6,7)]
  colnames(cl_reform0) = colnames(cl_reform)
  cl_reform = rbind(cl_reform, cl_reform0)
  
  cl_reform = cl_reform[cl_reform$dir==1,]
  
  dec_cl = dec_l[[n]]
  
  mat_lig_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  mat_rec_cl = data.frame(matrix(NA, nrow = length(unique(cl_reform$id_cp_interaction)), 
                   ncol = length(unique(cl_reform$ct1))))
  colnames(mat_lig_cl) = colnames(mat_rec_cl) = unique(cl_reform$ct1)
  rownames(mat_lig_cl) = rownames(mat_rec_cl) = unique(cl_reform$id_cp_interaction)
  for(i in 1:nrow(cl_reform)){
    g1 = cl_reform[i,"lr1"]
    g2 = cl_reform[i,"lr2"]
    
    c1 = cl_reform[i,"ct1"]
    c2 = cl_reform[i,"ct2"]
    
    int = as.character(cl_reform[i,1])
    
    m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g1,c1], na.rm = T)
    if(is.na(m1)) m1 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g1,c1], 
                            na.rm = T)
    mat_lig_cl[int, c1] = m1
    
    m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,1]==g2,c2], na.rm = T)
    if(is.na(m2)) m2 = mean(dec_cl[dec_cl$id_cp_interaction==int & dec_cl[,5]==g2,c2],
                            na.rm = T)
    if(is.na(m1) | is.na(m2)) print(int)
    mat_rec_cl[int, c2] = m2
  }
  
  # ligand vs receptor correlation
  cor_mat_each_cl = cor(mat_lig_cl, mat_rec_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]] = list("mat_lig_ct" = mat_lig_cl, "mat_rec_ct" = mat_rec_cl,
                               "cor_mat_each" = cor_mat_each_cl)
  
  # sum(lig, reg) correlation
  mat_lig_cl[is.na(mat_lig_cl)] = 0
  mat_rec_cl[is.na(mat_rec_cl)] = 0
  mat_both_cl = mat_lig_cl+mat_rec_cl
  mat_both_cl[mat_both_cl==0] = NA
  cor_mat_both_cl = cor(mat_both_cl, use="pairwise.complete.obs", method = "sp")
  
  scoring_mats_dir[[n]]$cor_mat_both = cor_mat_both_cl
}
saveRDS(scoring_mats_dir, file = "results/cell_comm/updt/scoring_mats_dir.RDS")

Interaction analysis in conditions

Compare interactions with DE genes

inter_counts_dir = list()
for(n in names(reform_list)){
  inter_counts_dir[[n]] = list()
  for(i in c(0,1)){
    cl_reform = reform_list[[n]]
    cl_reform = cl_reform[cl_reform$dir==i,]
    
    n_dir_int = matrix(0, nrow = length(unique(cl_reform$ct1)),
                       ncol = length(unique(cl_reform$ct1)))
    rownames(n_dir_int) = colnames(n_dir_int) = unique(cl_reform$ct1)
    for(c1 in unique(cl_reform$ct1)){
      for(c2 in unique(cl_reform$ct1)){
        n_dir_int[c1,c2] = nrow(cl_reform[cl_reform$ct1==c1 & cl_reform$ct2==c2,])
        if(i==0){
          n_dir_int[c1,c2] = n_dir_int[c1,c2]+nrow(cl_reform[cl_reform$ct1==c2 &
                                                               cl_reform$ct2==c1,])
        }
      }
    }
    inter_counts_dir[[n]][[as.character(i)]] = n_dir_int
  }
}
saveRDS(inter_counts_dir, file = "results/cell_comm/updt/inter_counts_dir.RDS")

Plot interaction heatmap - total and filtered

# add genes from complexes
complex_ref = unique(rbind(dec_l$healthy[dec_l$healthy$is_complex=="True",c(1,5)], 
                           dec_l$embolised[dec_l$embolised$is_complex=="True",c(1,5)],
                           dec_l$regenerating[dec_l$regenerating$is_complex=="True",c(1,5)],
                           dec_l$healthy_simp[dec_l$healthy_simp$is_complex=="True",c(1,5)],
                           dec_l$embolised_simp[dec_l$embolised_simp$is_complex=="True",c(1,5)],
                           dec_l$regenerating_simp[dec_l$regenerating_simp$is_complex=="True",c(1,5)]))
inter_df = list()
for(n in names(reform_list)){
  tmp = merge(reform_list[[n]], complex_ref, by.x = 4, by.y = 2, all.x = T)[,c(2,3,4,1,5,8,6,7)]
  inter_df[[n]] = merge(tmp, complex_ref, by.x = 5, by.y = 2, all.x = T)[,c(2,3,4,5,6,1,9,7,8)]
  inter_df[[n]]$gene_name.x[is.na(inter_df[[n]]$gene_name.x)] = inter_df[[n]]$lr1[is.na(inter_df[[n]]$gene_name.x)]
  inter_df[[n]]$gene_name.y[is.na(inter_df[[n]]$gene_name.y)] = inter_df[[n]]$lr2[is.na(inter_df[[n]]$gene_name.y)]
  colnames(inter_df[[n]])[c(5,7)] = c("gn1", "gn2")
}

for(cc in names(inter_df)){
  # interaction unique in condition
  unique_inter = setdiff(unique(inter_df[[cc]]$id_cp_interaction),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$id_cp_interaction,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$id_cp_interaction)))
  inter_df[[cc]]$cpdb_unique = inter_df[[cc]]$id_cp_interaction %in% unique_inter
  
  # lr1 unique in condition
  unique_lr1 = setdiff(unique(inter_df[[cc]]$lr1),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr1,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr1)))
  inter_df[[cc]]$cpdb_unique_lr1 = inter_df[[cc]]$lr1 %in% unique_lr1
  
  # lr2 unique in condition
  unique_lr2 = setdiff(unique(inter_df[[cc]]$lr2),
                         unique(c(inter_df[[names(inter_df)[names(inter_df)!=cc][1]]]$lr2,
                                  inter_df[[names(inter_df)[names(inter_df)!=cc][2]]]$lr2)))
  inter_df[[cc]]$cpdb_unique_lr2 = inter_df[[cc]]$lr2 %in% unique_lr2
}
for(cc in names(inter_df)){
  oc1 = names(inter_df)[names(inter_df)!=cc][1]
  oc2 = names(inter_df)[names(inter_df)!=cc][2]
  int1 = paste0(inter_df[[cc]]$id_cp_interaction,inter_df[[cc]]$ct1,inter_df[[cc]]$ct2)
  int2 = unique(paste0(inter_df[[oc1]]$id_cp_interaction,inter_df[[oc1]]$ct1,inter_df[[oc1]]$ct2))
  int3 = unique(paste0(inter_df[[oc2]]$id_cp_interaction,inter_df[[oc2]]$ct1,inter_df[[oc2]]$ct2))
  unique_inter = setdiff(unique(int1), unique(c(int2, int3)))
  inter_df[[cc]]$cpdb_unique_ct = int1 %in% unique_inter
}
for(n in names(inter_df)){
  df = inter_df[[n]]
  df = unique(df[,c(1:3)])
  
  ints_un_ct = rowSums(table(df$id_cp_interaction, df$ct1)>0)<4 | 
    rowSums(table(df$id_cp_interaction, df$ct2)>0)<4
  
  inter_df[[n]]$inter_ct_spec = inter_df[[n]]$id_cp_interaction %in% names(ints_un_ct)[ints_un_ct]
}
for(n in names(inter_df)){
  xxx = data.frame(ct = c(inter_df[[n]][,2], inter_df[[n]][,3]), 
                   lr = c(inter_df[[n]][,4], inter_df[[n]][,6]))
  xxx = unique(xxx)
  tab_lr = (table(xxx$lr, xxx$ct)>0)*1
  tab_lr = rowSums(tab_lr)
  
  inter_df[[n]]$lr1_nct = tab_lr[inter_df[[n]]$lr1]
  inter_df[[n]]$lr2_nct = tab_lr[inter_df[[n]]$lr2]
}
saveRDS(inter_df, file = "results/cell_comm/updt/cond_diff_interact_DE.RDS")

Get non-ubiquitous interactions


int_counts_total = list()
int_counts_filt = list()
for(n in names(inter_df)){
  subdfct = unique(inter_df[[n]][,1:3])
  subdfct = unique(subdfct)
  dfct = data.frame("ct1" = c(subdfct$ct1, subdfct$ct2),
                    "ct2" = c(subdfct$ct2, subdfct$ct1))
  int_counts_total[[n]] = dfct
  pheatmap::pheatmap(table(dfct$ct1, dfct$ct2), main = n)
  
  keep = inter_df[[n]]$lr1_nct<=1 | inter_df[[n]]$lr2_nct<=1
  subdfct = unique(inter_df[[n]][keep,c(1:3, 15,16)])
  swp = subdfct[,5]==1
  tmp = subdfct$ct2[swp]
  subdfct$ct2[swp] = subdfct$ct1[swp]
  subdfct$ct1[swp] = tmp
  tmp = subdfct$lr2_nct[swp]
  subdfct$lr2_nct[swp] = subdfct$lr1_nct[swp]
  subdfct$lr1_nct[swp] = tmp
  dup = subdfct[subdfct$lr1_nct==1 & subdfct$lr2_nct==1,]
  tmp = dup$ct2
  dup$ct2 = dup$ct1
  dup$ct1 = tmp
  subdfct = rbind(subdfct, dup)
  subdfct = unique(subdfct[,1:3])
  int_counts_filt[[n]] = dfct
  pheatmap::pheatmap(table(subdfct$ct1, subdfct$ct2), main = n)
}
saveRDS(int_counts_total, file = "results/cell_comm/updt/int_counts_total.RDS")

saveRDS(int_counts_filt, file = "results/cell_comm/updt/int_counts_filt.RDS")

Annotate interactions

inter_nonss = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$healthy$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy$id_cp_interaction,
                               inter_df$regenerating$id_cp_interaction)),
                      unique(inter_df$embolised$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised$id_cp_interaction,
                               inter_df$healthy$id_cp_interaction)),
                      unique(inter_df$regenerating$id_cp_interaction))

ct_g_cond = list()
for(n in names(inter_df)[1:3]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  ct_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
ct_g_cond = Reduce(rbind, ct_g_cond)
dim(ct_g_cond)
[1] 13141     6
length(unique(ct_g_cond$interact))
[1] 177
inter_nonss = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$healthy_simp$id_cp_interaction))
inter_nonemb = setdiff(unique(c(inter_df$healthy_simp$id_cp_interaction,
                               inter_df$regenerating_simp$id_cp_interaction)),
                      unique(inter_df$embolised_simp$id_cp_interaction))
inter_nonreg = setdiff(unique(c(inter_df$embolised_simp$id_cp_interaction,
                               inter_df$healthy_simp$id_cp_interaction)),
                      unique(inter_df$regenerating_simp$id_cp_interaction))

cts_g_cond = list()
for(n in names(inter_df)[4:6]){
  df = inter_df[[n]][inter_df[[n]]$cpdb_unique | 
                       inter_df[[n]]$id_cp_interaction %in% c(inter_nonss, inter_nonemb, inter_nonreg),]
  #df = df[df$lr1_nct<=1 | df$lr2_nct<=1,]
  
  cts = unique(c(df$ct1, df$ct2))
  ct_g_list = list()
  for(ct in cts){
    ct_g_list[[ct]] = data.frame("interact" = c(as.character(df$id_cp_interaction[df$ct1==ct]),
                                                as.character(df$id_cp_interaction[df$ct2==ct])),
                                 "gene" = c(as.character(df$gn1[df$ct1==ct]),
                                            as.character(df$gn2[df$ct2==ct])),
                                 "gene_target" = c(as.character(df$gn2[df$ct1==ct]),
                                                   as.character(df$gn1[df$ct2==ct])),
                                 "ct" = ct,
                                 "ct_target" = c(as.character(df$ct2[df$ct1==ct]),
                                                as.character(df$ct1[df$ct2==ct])),
                                 "cond" = n, stringsAsFactors = F)
  }
  cts_g_cond[[n]] = unique(Reduce(rbind, ct_g_list))
}
cts_g_cond = Reduce(rbind, cts_g_cond)
dim(cts_g_cond)
[1] 10449     6
length(unique(cts_g_cond$interact))
[1] 178
ct_g_l = list("cts_g_cond" = cts_g_cond,
              "ct_g_cond" = ct_g_cond)

Get expression for each interaction in each condition

inter_annot = read.csv("results/cell_comm/updt/inter_unique5.csv", header = T, stringsAsFactors = F)
#xxx = merge(unique(rbind(ct_g_cond[,1:3], cts_g_cond[,1:3])), 
#            unique(inter_annot[,c(1,4)]), by = 1, all.x = T)
#write.csv(xxx, file = "inter_unique5.csv", row.names = F, col.names = T, quote = F)

inter_annot = unique(inter_annot[,c(1,4)])

description = strsplit(inter_annot$description, ";")
inter_des = lapply(1:length(description), 
                   function(x) rep(inter_annot$interact[x], length(description[[x]])))
inter_annot = data.frame("inter" = unlist(inter_des),
                         "funct" = unlist(description))

inter_annot$funct[inter_annot$funct=="intercellular adhesion"] = "adhesion"
inter_annot$funct[inter_annot$funct=="antibody regulation"] = "immune regulation"
inter_annot$funct[inter_annot$funct=="antigen presentation"] = "immune activity"

write.csv(inter_annot, file = "data/interaction_annotation2.csv", 
          row.names = F, col.names = T, quote = F)
attempt to set 'col.names' ignored
colnames(inter_annot) = c("interact", "description")

Variability of interactions

ct_int_exp_l = list()
for(simp in c(T, F)){
  n_use = names(exp_list)[grepl("simp", names(exp_list))==simp]
  #ct_int_exp = cbind(exp_list[[n_use[1]]], exp_list[[n_use[3]]]$value, exp_list[[n_use[3]]]$value)
  ct_int_exp = merge(exp_list[[n_use[1]]], exp_list[[n_use[2]]], by = 1:6)
  ct_int_exp = merge(ct_int_exp, exp_list[[n_use[3]]], by = 1:6)
  ct_int_exp$value.x[is.na(ct_int_exp$value.x)] = ct_int_exp$value.y[is.na(ct_int_exp$value.y)] = ct_int_exp$value[is.na(ct_int_exp$value)] = 0
  colnames(ct_int_exp)[7:9] = c("healthy_exp", "embolised_exp", "regenerating_exp")
  ct_int_exp = ct_int_exp[ct_int_exp$healthy_exp>0 | 
                            ct_int_exp$embolised_exp>0 | 
                            ct_int_exp$regenerating_exp>0,]

  tup_list = list()
  keep_row = c()
  for(i in 1:nrow(ct_int_exp)){
    tup1 = paste(c(ct_int_exp$gene[i], ct_int_exp$gene_target[i], ct_int_exp$ct[i], 
                   ct_int_exp$ct_target[i], ct_int_exp$cond[i]), collapse = " ")
    tup2 = paste(c(ct_int_exp$gene_target[i], ct_int_exp$gene[i], ct_int_exp$ct_target[i], 
                   ct_int_exp$ct[i], ct_int_exp$cond[i]), collapse = " ")
    if(!(tup1 %in% tup_list) & !(tup2 %in% tup_list)){
      tup_list = c(tup_list, tup1, tup2)
      keep_row = c(keep_row, T)
    } else{
      keep_row = c(keep_row, F)
    }
  }
  ct_int_exp = ct_int_exp[keep_row,]
  
  ct_int_exp = merge(ct_int_exp, unique(ct_g_cond_ann[,c(1,4)]), by = 1, all = T)
  nn = if(simp) "simp" else "all"
  ct_int_exp_l[[nn]] = ct_int_exp
  
  ct_int_exp_l[[nn]]$cond = unlist(lapply(strsplit(ct_int_exp_l[[nn]], "_"), function(x) x[1]))
}
Error in strsplit(ct_int_exp_l[[nn]], "_") : non-character argument

GSEA of interactions using mutual information

inter_annot = read.csv("data/interaction_annotation2.csv", header = T)
inter_annot$funct[grepl("imm", inter_annot$funct)] = "immune"
inter_annot$funct[grepl("inflam", inter_annot$funct)] = "immune"
gr = inter_annot$funct
names(gr) = inter_annot$inter
gr_list = tapply(inter_annot$inter, inter_annot$funct, function(x) x)

her_allint_l = list()
for(simp in c(T, F)){
  n_use = names(reform_list)[grepl("simp", names(reform_list))==simp]
  
  he_allint = merge(reform_list[[n_use[1]]][,1:6], reform_list[[n_use[2]]][,1:6], by = 1:3, all = T)
  he_allint$lr1.x[is.na(he_allint$lr1.x)] = he_allint$lr1.y[is.na(he_allint$lr1.x)]
  he_allint$lr2.x[is.na(he_allint$lr2.x)] = he_allint$lr2.y[is.na(he_allint$lr2.x)]
  he_allint = he_allint[,c(1:6,9)]
  her_allint = merge(he_allint, reform_list[[n_use[3]]][,1:6], by = 1:3, all = T)
  her_allint$lr1.x[is.na(her_allint$lr1.x)] = her_allint$lr1[is.na(her_allint$lr1.x)]
  her_allint$lr2.x[is.na(her_allint$lr2.x)] = her_allint$lr2[is.na(her_allint$lr2.x)]
  her_allint = her_allint[,c(1:7,10)]
  her_allint[is.na(her_allint)] = 0
  colnames(her_allint)[4:8] = c("lr1", "lr2", "healthy_exp", "embolised_exp", "regenerating_exp")
  
  # count occurrences per condition. this works bc we're already working with sig means
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$healthy_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, data.frame(table(her_allint$id_cp_interaction[her_allint$embolised_exp>0])), 
                                            by = 1, all.x = T)
  her_allint = merge(her_allint, 
                     data.frame(table(her_allint$id_cp_interaction[her_allint$regenerating_exp>0])), 
                     by = 1, all.x = T)
  colnames(her_allint)[9:11] = c("healthy_n", "embolised_n", "regenerating_n")
  her_allint[is.na(her_allint)] = 0
  her_allint$ct_pair = factor(paste0(her_allint$ct1, "_", her_allint$ct2))
  
  comb_cond = combn(colnames(her_allint)[6:8],2)
  colnames(comb_cond) = c("he", "hr", "er")
  for(i in colnames(comb_cond)){
    plot_df = her_allint[her_allint[,comb_cond[1,i]]>0 | her_allint[,comb_cond[2,i]]>0,1:12]
    
    exp_df1 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[1,i], fill = 0)
    rownames(exp_df1) = exp_df1[,1]
    exp_df1 = exp_df1[,-1]>0
    exp_df2 = reshape2::dcast(plot_df, formula = id_cp_interaction ~ ct_pair, 
                              value.var = comb_cond[2,i], fill = 0)
    rownames(exp_df2) = exp_df2[,1]
    exp_df2 = exp_df2[,-1]>0
    
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) infotheo::mutinformation(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,], exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) e1071::hamming.distance(exp_df1[x,],
                                                                        exp_df2[x,])/sum(exp_df1[x,] |
                                                                                           exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    plot_df = merge(plot_df, sapply(rownames(exp_df1), 
                                    function(x) sum(exp_df1[x,] | exp_df2[x,])),
                    by.x = 1, by.y = 0, all.x = T)
    
    colnames(plot_df)[13:16] = paste0(c("mutInfo_", "hamm_", "hammNorm_", "tot_"), i)
    
    her_allint = merge(her_allint, unique(plot_df[,c(1,13:16)]), all.x = T, by = 1)
  }
  her_allint$mutInfo_er[is.na(her_allint$mutInfo_er)] = 1
  her_allint$mutInfo_hr[is.na(her_allint$mutInfo_hr)] = 1
  her_allint$mutInfo_he[is.na(her_allint$mutInfo_he)] = 1
  her_allint$tot_he[is.na(her_allint$tot_he)] = 0
  her_allint$tot_hr[is.na(her_allint$tot_hr)] = 0
  her_allint$tot_er[is.na(her_allint$tot_er)] = 0
  her_allint$hamm_er[is.na(her_allint$hamm_er)] = 0
  her_allint$hamm_hr[is.na(her_allint$hamm_hr)] = 0
  her_allint$hamm_he[is.na(her_allint$hamm_he)] = 0
  her_allint$hammNorm_er[is.na(her_allint$hammNorm_er)] = 0
  her_allint$hammNorm_he[is.na(her_allint$hammNorm_he)] = 0
  her_allint$hammNorm_hr[is.na(her_allint$hammNorm_hr)] = 0
  
  her_allint$diff_n_he = her_allint$embolised_n-her_allint$healthy_n
  her_allint$diff_n_hr = her_allint$regenerating_n-her_allint$healthy_n
  her_allint$diff_n_er = her_allint$regenerating_n-her_allint$embolised_n
  
  # plot tot vs mutual
  plot_df = unique(her_allint[,c("id_cp_interaction", paste0(c("mutInfo_", "tot_", "diff_n_"), i))])
  plt = ggplot(plot_df, aes(x = tot_er, y = mutInfo_er*(diff_n_er/abs(diff_n_er))))+
    geom_bin2d()+
    scale_x_log10()+
    theme_bw()+
    theme(aspect.ratio = 1)
  print(plt)
  
  nn = if(simp) "simp" else "all"
  her_allint_l[[nn]] = her_allint
}
Aggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the resultAggregation function missing: defaulting to length
Aggregation function missing: defaulting to length
column names ‘y.x’, ‘y.y’ are duplicated in the result

saveRDS(her_allint_l, file = "./results/cell_comm/updt/interactions_mutInfo_condComp.RDS")

Save mutual information for LR and cell types

for(n in names(her_allint_l)){
  her_allint = her_allint_l[[n]]
  # select genes from lowest mutual (table - get most common)
  # +
  # mean mutual per cell type (lowest = more change)
  ## plot interactions based on those genes (some are involved in more than one)
  ## heatmap - rows ct; columns genes; gaps between interact; 1 heatmap/cond, same ct ordering
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","ct2","mutInfo_he")])
  ct_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","ct2","mutInfo_hr")])
  ct_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","ct2","mutInfo_er")])
  ct_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$ct1, sub_df2$ct2), mean)
  ct_mut_df = cbind(ct_mut_he,ct_mut_hr,ct_mut_er)
  rownames(ct_mut_df) = names(ct_mut_he)
  colnames(ct_mut_df) = c("he", "hr", "er")
  saveRDS(ct_mut_df, file = "./results/cell_comm/updt/ct_select_mutInfo_condComp.RDS")
  
  sub_df1 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr1","mutInfo_he")])
  sub_df2 = unique(her_allint[her_allint$tot_he>0,c("id_cp_interaction","lr2","mutInfo_he")])
  lr_mut_he = tapply(c(sub_df1$mutInfo_he, sub_df2$mutInfo_he), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr1","mutInfo_hr")])
  sub_df2 = unique(her_allint[her_allint$tot_hr>0,c("id_cp_interaction","lr2","mutInfo_hr")])
  lr_mut_hr = tapply(c(sub_df1$mutInfo_hr, sub_df2$mutInfo_hr), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  sub_df1 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr1","mutInfo_er")])
  sub_df2 = unique(her_allint[her_allint$tot_er>0,c("id_cp_interaction","lr2","mutInfo_er")])
  lr_mut_er = tapply(c(sub_df1$mutInfo_er, sub_df2$mutInfo_er), 
                     c(sub_df1$lr1, sub_df2$lr2), mean)
  
  lr_cnt_he = table(c(her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr1"],
                      her_allint[her_allint$tot_he>0 & her_allint$mutInfo_he<=0.05,"lr2"]))
  lr_he = merge(lr_cnt_he, lr_mut_he, by.x = 1, by.y = 0)
  lr_cnt_hr = table(c(her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr1"],
                      her_allint[her_allint$tot_hr>0 & her_allint$mutInfo_hr<=0.05,"lr2"]))
  lr_hr = merge(lr_cnt_hr, lr_mut_hr, by.x = 1, by.y = 0)
  lr_cnt_er = table(c(her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr1"],
                      her_allint[her_allint$tot_er>0 & her_allint$mutInfo_er<=0.05,"lr2"]))
  lr_er = merge(lr_cnt_er, lr_mut_er, by.x = 1, by.y = 0)
  
  # ECM - which proteins/collagens?; mention TGFB
  # dev - which ligands/receptors
  lr_all = rbind(lr_he, lr_hr, lr_er)
  lr_all$cond = c(rep("he", nrow(lr_he)), rep("hr", nrow(lr_hr)),rep("er", nrow(lr_er)))
  colnames(lr_all) = c("lr", "n_mut.05", "mean_mutInfo", "cond")
  saveRDS(lr_all, file = paste0("./results/cell_comm/updt/", n, "LR_select_mutInfo_condComp.RDS"))
}

Plot interactions

for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  r = if(n=="all") 1:3 else 4:6
  
  # interactions
  intdf = unique(Reduce(rbind, reform_list[r])[,c(1,4,5)])
  intdf$intpair = paste0(intdf$lr1, " - ", intdf$lr2)
  
  ct_int_exp_file = unique(ct_int_exp[,c(1,4:5,7:10)])
  ct_int_exp_file$ctpair = paste0(ct_int_exp_file$ct, " - ", ct_int_exp_file$ct_target)
  
  plot_df_int = reshape2::melt(ct_int_exp_file[,c(1,8,4:7)])
  plot_df_int$variable = unlist(lapply(strsplit(as.character(plot_df_int$variable), "_"), 
                                       function(x) x[[1]][1]))
  plot_df_int$variable = factor(plot_df_int$variable, 
                                levels = rev(c("healthy", "embolised", "regenerating")))
  
  sub_plot_df_int = plot_df_int[grepl("Stellate", plot_df_int$ctpair) |
                                  grepl("Kupffer", plot_df_int$ctpair) |
                                  grepl("LSEC", plot_df_int$ctpair),]
  
  sub_plot_df_int = merge(sub_plot_df_int, intdf[,c(1,4)], by = 1, all.x = T)
  
  gg = "ECM"
  plt = ggplot(sub_plot_df_int[sub_plot_df_int$description==gg,], 
         aes(x = ctpair, y = variable, colour = value, size = value))+
    facet_grid(intpair~.)+
    guides(size = guide_legend(title = "exp", reverse = T), 
           colour = guide_legend(title = "exp", reverse = T))+
    geom_point()+
    labs(title = gg)+
    theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
          strip.text.y = element_text(angle = 0, size = 8))
  print(plt)
}

Important tables

for(n in names(inter_df)){
  write.csv(inter_df[[n]], col.names = T, row.names = F, quote = F,
            file = paste0("results/cell_comm/updt/tables/Interact_", n, "_celltypes_cond.csv"))
}
for(n in names(ct_int_exp_l)){
  ct_int_exp = ct_int_exp_l[[n]]
  write.csv(ct_int_exp, col.names = T, row.names = F, quote = F, 
            file = paste0("results/cell_comm/updt/tables/", n, "interact_celltype_exp_group.csv"))
}

Cell-cell communication networks

Load data to make cell comm networks (NOT USED HERE)

redone_meta = list()
for(cc in unique(allcells_css$Condition)){
  redone_meta[[cc]] = read.table(paste0("results/cell_comm/CellPhoneDB_runs_updt/", 
                                        cc, "/", cc, "_meta_names.txt"), 
                                 sep = "\t", header = T, row.names = 1)
}
redone_meta_all = Reduce(rbind, redone_meta)

allcells_redone = AddMetaData(allcells_css, redone_meta_all)
allcells_redone = allcells_redone[,!is.na(allcells_redone$cell_type)]

Functions used to make cell comm networks

makeMedian = function(point_df, edge_df, cl = c("ct2", "maj_g1", "maj_g2")){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  maj_mat = table(edge_df[,cl[2]], edge_df[,cl[3]])
  diag(maj_mat) = 0
  edge_major = data.frame(maj_mat + t(maj_mat))
  edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
  edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
  
  clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
  keep = c()
  for(j in 1:ncol(clcomb)){
    keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
  }
  edge_major = edge_major[keep,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

makeMedianCond = function(point_df, edge_df, cl = c("ct", "ct_g1", "ct_g2"), edge_by = "condition"){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  edge_l = list()
  for(i in unique(edge_df[,edge_by])){
    sub_edge_df = edge_df[edge_df[,edge_by]==i,]
    maj_mat = table(sub_edge_df[,cl[2]], sub_edge_df[,cl[3]])
    diag(maj_mat) = 0
    edge_major = data.frame(maj_mat + t(maj_mat))
    edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
    edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
    
    # remove repeated
    clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
    keep = c()
    for(j in 1:ncol(clcomb)){
      keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
    }
    
    edge_l[[i]] = edge_major[keep,]
  }
  edge_major = Reduce(rbind, edge_l)
  edge_major[,edge_by] = unlist(lapply(names(edge_l), function(x) rep(x, nrow(edge_l[[x]]))))
  edge_major = edge_major[edge_major$Var2!=edge_major$Var1,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

Plot ligands and receptors with MDS

makeMedian = function(point_df, edge_df, cl = c("ct2", "maj_g1", "maj_g2")){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  maj_mat = table(edge_df[,cl[2]], edge_df[,cl[3]])
  diag(maj_mat) = 0
  edge_major = data.frame(maj_mat + t(maj_mat))
  edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
  edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
  
  clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
  keep = c()
  for(j in 1:ncol(clcomb)){
    keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
  }
  edge_major = edge_major[keep,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

makeMedianCond = function(point_df, edge_df, cl = c("ct", "ct_g1", "ct_g2"), edge_by = "condition"){
  mean_major = data.frame("X1" = tapply(point_df$X1, point_df[,cl[1]], median),
                          "X2" = tapply(point_df$X2, point_df[,cl[1]], median))
  mean_major[,cl[1]] = rownames(mean_major)
  
  edge_df[,cl[2]] = factor(edge_df[,cl[2]], levels = unique(c(edge_df[,cl[2]], edge_df[,cl[3]])))
  edge_df[,cl[3]] = factor(edge_df[,cl[3]], levels = unique(c(as.character(edge_df[,cl[2]]),
                                                              edge_df[,cl[3]])))
  
  edge_l = list()
  for(i in unique(edge_df[,edge_by])){
    sub_edge_df = edge_df[edge_df[,edge_by]==i,]
    maj_mat = table(sub_edge_df[,cl[2]], sub_edge_df[,cl[3]])
    diag(maj_mat) = 0
    edge_major = data.frame(maj_mat + t(maj_mat))
    edge_major = merge(edge_major, mean_major, by.x = 1, by.y = 3)
    edge_major = merge(edge_major, mean_major, by.x = 2, by.y = 3)
    
    # remove repeated
    clcomb = combn(unique(c(as.character(edge_major$Var1), as.character(edge_major$Var2))), 2)
    keep = c()
    for(j in 1:ncol(clcomb)){
      keep = c(keep, which(edge_major$Var2==clcomb[1,j] & edge_major$Var1==clcomb[2,j]))
    }
    
    edge_l[[i]] = edge_major[keep,]
  }
  edge_major = Reduce(rbind, edge_l)
  edge_major[,edge_by] = unlist(lapply(names(edge_l), function(x) rep(x, nrow(edge_l[[x]]))))
  edge_major = edge_major[edge_major$Var2!=edge_major$Var1,]
  
  return(list(mean_major = mean_major, edge_major = edge_major))
}

Save network objects

inter_df = readRDS(file = "results/cell_comm/updt/cond_diff_interact_DE.RDS")

# interactions unique to each condition
unique_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$embolised$id_cp_interaction, 
                          c(inter_df$healthy$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                  setdiff(inter_df$regenerating$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction)))

# interactions unique to healthy or to emb/regen
comph_inters = c(setdiff(inter_df$healthy$id_cp_interaction, 
                          c(inter_df$embolised$id_cp_interaction, inter_df$regenerating$id_cp_interaction)),
                 setdiff(inter_df$embolised$id_cp_interaction, inter_df$healthy$id_cp_interaction),
                 setdiff(inter_df$regenerating$id_cp_interaction, inter_df$healthy$id_cp_interaction))

# prepare gene pairs per condition
gene_pairs_cond = rbind(unique(inter_df$healthy[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$embolised[,c("id_cp_interaction", "gn1", "gn2")]),
                        unique(inter_df$regenerating[,c("id_cp_interaction", "gn1", "gn2")]))
gene_pairs_cond$condition = c(rep("healthy", nrow(unique(inter_df$healthy[,c("id_cp_interaction", 
                                                                             "gn1", "gn2")]))),
                              rep("embolised", nrow(unique(inter_df$embolised[,c("id_cp_interaction", 
                                                                                 "gn1", "gn2")]))),
                              rep("regenerating", nrow(unique(inter_df$regenerating[,c("id_cp_interaction",
                                                                                       "gn1", "gn2")]))))

# list all LR genes
all_lr_genes = unique(c(as.character(inter_df$healthy$gn1), as.character(inter_df$healthy$gn2),
                        as.character(inter_df$embolised$gn1), as.character(inter_df$embolised$gn2),
                        as.character(inter_df$regenerating$gn1), as.character(inter_df$regenerating$gn2)))
all_lr_genes = all_lr_genes[all_lr_genes %in% rownames(sub_allcells_css@assays$SCT@data)]

# calculate mean per cell type and condition for each LR gene
mean_exp_cond_lr = apply(sub_allcells_css@assays$SCT@data[all_lr_genes,], 1, 
                         function(x) tapply(x, paste0(sub_allcells_css$subpops, 
                                                      "_", sub_allcells_css$Condition), mean))

# determine the cell type and condition with the highest expression
max_cond_ct = rownames(mean_exp_cond_lr)[apply(mean_exp_cond_lr, 2, which.max)]
names(max_cond_ct) = colnames(mean_exp_cond_lr)
max_cond_ct_ct = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[1]))
max_cond_ct_cond = unlist(lapply(strsplit(max_cond_ct, "_"), function(x) x[2]))

# correlation of mean expression
cor_cond_lr = cor(mean_exp_cond_lr, method = "sp")

# filter correlation with itself, keep only genes with cor>=0.3
diag(cor_cond_lr) = 0
adj_cond_mat = cor_cond_lr>=0.3

# build graph, project with MDS
network_cond = graph_from_adjacency_matrix(adj_cond_mat, weighted=T, mode="undirected", diag=F)
l_cond = igraph::layout_with_mds(network_cond)
l_cond = data.frame(l_cond)
l_cond$gene = colnames(adj_cond_mat)
rownames(l_cond) = colnames(adj_cond_mat)

# define all edges, based on CellPhoneDB pairings
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_df)[9:10] = c("ct_g1", "ct_g2")
# add highest expressing major cell types
edge_cond_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g1), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_df$ct_g2), "Cholangiocytes", 
                                           "Immune"))))
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_df = merge(edge_cond_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)

# define the vertices of the network
point_cond_df = l_cond
point_cond_df$ct = max_cond_ct_ct[point_cond_df$gene]
point_cond_df$ct2 = ifelse(grepl("LSEC", point_cond_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_df$cond = max_cond_ct_cond[point_cond_df$gene]

# define the median points for each cell type (using max expression)
pe_l = makeMedian(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - FULL NETWORK
pe_cond_l = makeMedianCond(point_cond_df, edge_cond_df, cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l[[2]][pe_cond_l[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, 
                               size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l[["leg"]] = cowplot::get_legend(plt_cond_l[["healthy"]])

# plot FULL NETWORK median per condition
cowplot::plot_grid(plt_cond_l[[1]]+theme(legend.position = "none"), 
                   plt_cond_l[[2]]+theme(legend.position = "none"),
                   plt_cond_l[[3]]+theme(legend.position = "none"), plt_cond_l$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - UNIQUE PER CONDTION NETWORK
pe_cond_l_u = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% unique_inters,],
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_u = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_u[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_u[[2]][pe_cond_l_u[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_u[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_u[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_u[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_u[["leg"]] = cowplot::get_legend(plt_cond_l_u[["healthy"]])

# plot UNIQUE PER CONDTION NETWORK median per condition
cowplot::plot_grid(plt_cond_l_u[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_u[[2]]+theme(legend.position = "none"),
                   plt_cond_l_u[[3]]+theme(legend.position = "none"), plt_cond_l_u$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY NETWORK
pe_cond_l_h = makeMedianCond(point_cond_df, edge_cond_df[edge_cond_df$id_cp_interaction %in% inter_df$healthy$id_cp_interaction,], cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_h = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_h[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_h[[2]][pe_cond_l_h[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_h[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_h[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_h[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_h[["leg"]] = cowplot::get_legend(plt_cond_l_h[["healthy"]])

# plot HEALTHY NETWORK median per condition
cowplot::plot_grid(plt_cond_l_h[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_h[[2]]+theme(legend.position = "none"),
                   plt_cond_l_h[[3]]+theme(legend.position = "none"), plt_cond_l_h$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_cond_l_ch = makeMedianCond(point_cond_df, 
                             edge_cond_df[edge_cond_df$id_cp_interaction %in% comph_inters,], 
                             cl = c("ct", "ct_g1", "ct_g2"))

plt_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$condition)){
  plt_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_cond_l_ch[[2]][pe_cond_l_ch[[2]]$condition==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_cond_l_ch[["leg"]] = cowplot::get_legend(plt_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_cond_l_ch[[3]]+theme(legend.position = "none"), plt_cond_l_ch$leg, 
                   ncol = 2, rel_widths = c(1,1,1,0.5))

Plot ligands and receptors with UMAP

save(edge_cond_df, point_cond_df, file = "results/cell_comm/updt/networks_cond.RData")
save(pe_l, pe_cond_l, pe_cond_l_u, pe_cond_l_h, pe_cond_l_ch, 
     file = "results/cell_comm/updt/median_networks_cond.RData")

Save UMAP network objects

set.seed(2954)
l = uwot::umap(t(mean_exp_cond_lr), metric = "cosine", ret_nn = T, n_epochs = 1000)
l_cond = data.frame(l$embedding)
l_cond$gene = colnames(mean_exp_cond_lr)
rownames(l_cond) = colnames(mean_exp_cond_lr)
tmp_df = merge(gene_pairs_cond, l_cond, by.x = "gn1", by.y = "gene")
edge_cond_umap_df = merge(tmp_df, l_cond, by.x = "gn2", by.y = "gene")
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_ct), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[9:10] = c("ct_g1", "ct_g2")
edge_cond_umap_df$maj_g1 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g1), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g1), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g1), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g1),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df$maj_g2 = ifelse(grepl("LSEC", edge_cond_umap_df$ct_g2), "Endothelial",
                      ifelse(grepl("Hepatocytes", edge_cond_umap_df$ct_g2), "Hepatocytes",
                             ifelse(grepl("Stellate cells", edge_cond_umap_df$ct_g2), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", edge_cond_umap_df$ct_g2),
                                           "Cholangiocytes", "Immune"))))
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 1, by.y = 0, all.x = T)
edge_cond_umap_df = merge(edge_cond_umap_df, data.frame(max_cond_ct_cond), by.x = 2, by.y = 0, all.x = T)
colnames(edge_cond_umap_df)[4] = "cond"

point_cond_umap_df = l_cond
point_cond_umap_df$ct = max_cond_ct_ct[point_cond_umap_df$gene]
point_cond_umap_df$ct2 = ifelse(grepl("LSEC", point_cond_umap_df$ct), "Endothelial",
                      ifelse(grepl("Hepatocytes", point_cond_umap_df$ct), "Hepatocytes",
                             ifelse(grepl("Stellate cells", point_cond_umap_df$ct), "Mesenchymal",
                                    ifelse(grepl("Cholangiocytes", point_cond_umap_df$ct), "Cholangiocytes", 
                                           "Immune"))))
point_cond_umap_df$cond = max_cond_ct_cond[point_cond_umap_df$gene]


pe_umap_l = makeMedian(point_cond_umap_df, edge_cond_umap_df, cl = c("ct", "ct_g1", "ct_g2"))

# plot total gene correlation projection and median network
pltboth = ggplot()+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.25, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_segment(data = pe_umap_l[[2]], 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq), 
               alpha = 0.15, show.legend = F)+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


pltboth = ggplot()+
  geom_segment(data = edge_cond_umap_df, 
               mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y), 
               alpha = 0.03, show.legend = F)+
  geom_point(data = point_cond_umap_df, mapping = aes(x = X1, y = X2, colour = ct2), 
             alpha = 0.6, show.legend = F)+
  scale_shape_manual(values = c(0,4,19))+
  geom_point(data = pe_umap_l[[1]], 
             mapping = aes(x = X1, y = X2, fill = ct), 
             alpha = 1, pch = 21, size = 4)+
  scale_size_continuous(range = c(0, 4), limits = range(pe_l[[2]]$Freq))+
  theme_classic()+ theme(aspect.ratio = 1)
print(pltboth)


# get median per cell type, per condition - HEALTHY COMPARISON NETWORK
pe_umap_cond_l_ch = makeMedianCond(point_cond_umap_df, 
                                   edge_cond_umap_df[edge_cond_umap_df$id_cp_interaction%in%comph_inters,],
                                   edge_by = "cond", cl = c("ct", "ct_g1", "ct_g2"))

plt_umap_cond_l_ch = list()
for(cc in unique(pe_cond_l[[2]]$cond)){
  plt_umap_cond_l_ch[[cc]] = ggplot()+
    geom_segment(data = pe_umap_cond_l_ch[[2]][pe_umap_cond_l_ch[[2]]$cond==cc,], 
                 mapping = aes(x = X1.x, xend = X1.y, y = X2.x, yend = X2.y, size = Freq, alpha = Freq))+
    geom_point(data = pe_umap_cond_l_ch[[1]], 
               mapping = aes(x = X1, y = X2, fill = ct), 
               alpha = 1, pch = 21, size = 4)+
    scale_size_continuous(range = c(0, 4), limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    scale_alpha_continuous(limits = range(pe_umap_cond_l_ch[[2]]$Freq))+
    labs(title = cc)+
    guides(size = guide_legend(direction = "horizontal", nrow = 2),
           alpha = guide_legend(direction = "horizontal", nrow = 2))+
    theme_classic()
}
plt_umap_cond_l_ch[["leg"]] = cowplot::get_legend(plt_umap_cond_l_ch[["healthy"]])

# plot HEALTHY COMPARISON NETWORK median per condition
cowplot::plot_grid(plt_umap_cond_l_ch[[1]]+theme(legend.position = "none"), 
                   plt_umap_cond_l_ch[[2]]+theme(legend.position = "none"),
                   plt_umap_cond_l_ch[[3]]+theme(legend.position = "none"), plt_umap_cond_l_ch$leg, 
                   ncol = 4, rel_widths = c(1,1,1,0.5))

LS0tCnRpdGxlOiAiQ2VsbCBjb21tdW5pY2F0aW9uIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgR2VuZXJhbCBTZXR1cApTZXR1cCBjaHVuawoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gOCkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKCIuLiIpKQprbml0cjo6b3B0c19rbml0JGdldCgicm9vdC5kaXIiKQpgYGAKClNldHVwIHJldGljdWxhdGUKCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmtuaXRyOjprbml0X2VuZ2luZXMkc2V0KHB5dGhvbiA9IHJldGljdWxhdGU6OmVuZ19weXRob24pCnB5X2F2YWlsYWJsZShpbml0aWFsaXplID0gRkFMU0UpCnVzZV9weXRob24oU3lzLndoaWNoKCJweXRob24iKSkKcHlfY29uZmlnKCkKYGBgCgpMb2FkIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGRhdGEudGFibGUpCmBgYAoKCiMgTG9hZCBhbmQgcHJlcHJvY2VzcyBkYXRhCkxvYWQgZGF0YSAoZnJvbSBhbGwgY2VsbHMpCgpgYGB7cn0KYWxsY2VsbHNfY3NzID0gcmVhZFJEUyhmaWxlID0gImRhdGEvcHJvY2Vzc2VkL2FsbGNlbGxzX2Nzcy5SRFMiKQplbmRfY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9lbmRvdGhlbGlhbC9vbmx5X2VuZF9jZWxsc196b24uUkRTIikKaGVwX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvem9uYXRpb25fY29uZC9oZXBfY2VsbHNfem9uYXRpb25fcmFuay5SRFMiKQppbW1fY2VsbHMgPSByZWFkUkRTKGZpbGUgPSAicmVzdWx0cy9pbW11bmUvYWxsX2ltbV9jZWxscy5SRFMiKQpgYGAKClByZXBhcmUgYSBnbG9iYWwgb2JqZWN0IHdpdGggYWxsIG5lY2Vzc2FyeSBtZXRhZGF0YQoKYGBge3J9CmVuZHBvcF9kZiA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoZW5kX2NlbGxzKSwKICAgICAgICAgICAgICAgICAgICAgICAic3VicG9wcyIgPSBlbmRfY2VsbHNAbWV0YS5kYXRhJGVuZG9fc2ltcCkKaW1tcG9wX2RmID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBjb2xuYW1lcyhpbW1fY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgICJzdWJwb3BzIiA9IGltbV9jZWxsc0BtZXRhLmRhdGEkaW1tdW5lX2Fubm90KQpoZXBwb3BfZGYgPSBsYXBwbHkoaGVwX2NlbGxzLCBmdW5jdGlvbih4KSBjYmluZChjb2xuYW1lcyh4KSwgYXMuY2hhcmFjdGVyKHgkem9uYXRpb25faW50KSkpCmhlcHBvcF9kZiA9IFJlZHVjZShyYmluZCwgaGVwcG9wX2RmKQpoZXBwb3BfZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGhlcHBvcF9kZlssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgc3VicG9wcyA9IGZhY3RvcihoZXBwb3BfZGZbLDJdKSkKbGV2ZWxzKGhlcHBvcF9kZiRzdWJwb3BzKSA9IGMoIigtMC4wMDA5OSwwLjMzM10iID0gIkhlcGF0b2N5dGVzX1oxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIigwLjMzMywwLjY2N10iID0gIkhlcGF0b2N5dGVzX1oyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIigwLjY2NywxXSIgPSAiSGVwYXRvY3l0ZXNfWjMiKQpoZXBwb3BfZGYkc3VicG9wcyA9IGFzLmNoYXJhY3RlcihoZXBwb3BfZGYkc3VicG9wcykKCnN1YnBvcF9kZiA9IHJiaW5kKGVuZHBvcF9kZiwgaW1tcG9wX2RmLCBoZXBwb3BfZGYpCnN1YnBvcF9kZiRzdWJwb3BzW3N1YnBvcF9kZiRzdWJwb3BzPT0iQ3ljbGluZyBjZWxscyJdID0gIkRpdmlkaW5nIGVuZG90aGVsaWFsIGNlbGxzIgoKIyBhZGQgc3VicG9wIG1ldGFkYXRhCmFsbGNlbGxzX2NzcyA9IEFkZE1ldGFEYXRhKGFsbGNlbGxzX2Nzcywgc3VicG9wX2RmKQphbGxjZWxsc19jc3Mkc3VicG9wc1tpcy5uYShhbGxjZWxsc19jc3Mkc3VicG9wcyldID0gYWxsY2VsbHNfY3NzJGFsbGNlbGxzX21ham9yW2lzLm5hKGFsbGNlbGxzX2NzcyRzdWJwb3BzKV0KYWxsY2VsbHNfY3NzJHN1YnBvcHNbYWxsY2VsbHNfY3NzJHN1YnBvcHM9PSJIZXBhdG9jeXRlLU1vbm9jeXRlIGludGVyYWN0aW9uIl0gPSAiRG91YmxldHMiCgojIHRoZSBjZWxscyBpbiB0aGlzIG9iamVjdCBuYW1lZCAiSGVwYXRvY3l0ZXMiLCAiRW5kb3RoZWxpYWwgY2VsbHMiLCBhbmQgIkRvdWJsZXRzIiBoYXZlIHRvIGJlIHJlbW92ZWQKIyMgdGhlIGZpcnN0IHR3byBhcmUgY2VsbHMgdGhhdCBkaWRuJ3QgcGFzcyB0aGUgaGVwIGFuZCBlbmQgYW5hbHlzaXMKc3ViX2FsbGNlbGxzX2NzcyA9IGFsbGNlbGxzX2Nzc1ssIShhbGxjZWxsc19jc3Mkc3VicG9wcyAlaW4lIGMoIkhlcGF0b2N5dGVzIiwgIkVuZG90aGVsaWFsIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRvdWJsZXRzIikpXQoKIyBhZGQgc2ltcGxpZmllZCBjb2x1bW4gLSBtZXJnZSBzb21lIHBvcHVsYXRpb25zLCBkb24ndCBpbmNsdWRlIGN5Y2xpbmcgcG9wcyBhbmQgc3RyZXNzZWQgVCBjZWxscwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCA9IHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wcwpzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcFtncmVwbCgiTUFJVCIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTUFJVCBjZWxscyIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIk5LIGNlbGxzICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wKV0gPSAiTksgY2VsbHMiCnN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wW2dyZXBsKCJMU0VDIChoaWdoIE1UIiwgc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpXSA9ICJMU0VDIChoaWdoIE1UKSIKc3ViX2FsbGNlbGxzX2NzcyRzdWJwb3BzX3NpbXBbZ3JlcGwoIkNEOCBhYi1UICIsIHN1Yl9hbGxjZWxsc19jc3Mkc3VicG9wc19zaW1wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKV0gPSAiQ0Q4IGFiLVQgY2VsbHMiCnNpbXBfYWxsY2VsbHNfY3NzID0gc3ViX2FsbGNlbGxzX2Nzc1ssIShzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHNfc2ltcCAlaW4lIGMoImFiLVQgY2VsbHMgKHN0cmVzcykiLCAiRGl2aWRpbmcgY0RDcyIsICJEaXZpZGluZyBlbmRvdGhlbGlhbCBjZWxscyIsIkRpdmlkaW5nIFQvTksgY2VsbHMiKSldCmBgYAoKU3Vic2V0IGFuZCBwcm9jZXNzIGVhY2ggY29uZGl0aW9uCgpgYGB7cn0KI2NwZGJfcGF0aCA9ICJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9ydW5zX3VwZHQiCmNwZGJfcGF0aCA9ICIvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0IgoKY29uZF9jZWxscyA9IGxpc3QoKQpmb3IoY29uZCBpbiB1bmlxdWUoc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uKSl7CiAgIyBzdWJzZXQgY29uZGl0aW9uCiAgY29uZF9jZWxsc1tbY29uZF1dID0gc3ViX2FsbGNlbGxzX2Nzc1ssc3ViX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIAogICMgZGVmaW5lIG1ldGFkYXRhCiAgbWV0YSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgIkNlbGwiID0gcm93bmFtZXMoY29uZF9jZWxsc1tbY29uZF1dQG1ldGEuZGF0YSksIAogICAgICAgICAgICAgICAgICAgICJjZWxsX3R5cGUiID0gYXMuY2hhcmFjdGVyKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEkc3VicG9wcyksCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgd3JpdGUudGFibGUobWV0YSwgZmlsZSA9IHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIi8iLCBjb25kLCAiX21ldGFfbmFtZXMudHh0IiksIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCiAgCiAgIyBub3JtYWxpc2UgYW5kIHNhdmUKICBjb25kX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNvbmRfY2VsbHNbW2NvbmRdXSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIAogIGRhdCA9IGNiaW5kKHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpLAogICAgICAgICAgICAgIE1hdHJpeDo6YXMubWF0cml4KGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpKQogIGNvbG5hbWVzKGRhdClbMV0gPSAiR2VuZSIKICB3cml0ZS50YWJsZShkYXQsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICIvIiwgY29uZCwgIl9leHBfbm9ybS50eHQiKSwgCiAgICAgICAgICAgICAgc2VwID0gIlx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQogIAogIAogICMgc3Vic2V0IGNvbmRpdGlvbgogIGNvbmRfY2VsbHNbW2NvbmRdXSA9IHNpbXBfYWxsY2VsbHNfY3NzWyxzaW1wX2FsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uPT1jb25kXQogIAogICMgZGVmaW5lIG1ldGFkYXRhCiAgbWV0YV9zaW1wID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhjb25kX2NlbGxzW1tjb25kXV1AbWV0YS5kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJDZWxsIiA9IHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJjZWxsX3R5cGUiID0gYXMuY2hhcmFjdGVyKGNvbmRfY2VsbHNbW2NvbmRdXUBtZXRhLmRhdGEkc3VicG9wc19zaW1wKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIHdyaXRlLnRhYmxlKG1ldGFfc2ltcCwgZmlsZSA9IHBhc3RlMChjcGRiX3BhdGgsICIvIiwgY29uZCwgIl9zaW1wLyIsIGNvbmQsICJfbWV0YV9uYW1lcy50eHQiKSwgCiAgICAgICAgICAgIHNlcCA9ICJcdCIsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRikKICAgCiAgIyBub3JtYWxpc2UgYW5kIHNhdmUKICBjb25kX2NlbGxzW1tjb25kXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKGNvbmRfY2VsbHNbW2NvbmRdXSwgZG8uY29ycmVjdC51bWkgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcz1jKCJ1bmlxdWVfbmFtZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuZmVhdHVyZXMubiA9IE5VTEwpKQogIAogIGRhdCA9IGNiaW5kKHJvd25hbWVzKGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpLAogICAgICAgICAgICAgIE1hdHJpeDo6YXMubWF0cml4KGNvbmRfY2VsbHNbW2NvbmRdXUBhc3NheXMkU0NUQGRhdGEpKQogIGNvbG5hbWVzKGRhdClbMV0gPSAiR2VuZSIKICB3cml0ZS50YWJsZShkYXQsIGZpbGUgPSBwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIGNvbmQsICJfc2ltcC8iLCBjb25kLCAiX2V4cF9ub3JtLnR4dCIpLCAKICAgICAgICAgICAgICBzZXAgPSAiXHQiLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCn0KYGBgCgoKIyMgUnVubmluZyBDZWxsUGhvbmVEQgpDb21tYW5kcyBmb3IgcnVubmluIENlbGxQaG9uZURCIHdpbGwgYmUgd3JpdHRlbiBoZXJlLiBSZXN1bHRzIGFyZSBvdXRwdXQgdG8gdGhlIGBsb2NhbDFgIGRpc2sgdG8gYXZvaWQgSS9PIGlzc3VlcywgYnV0IHRoZSBvdXRwdXQgZm9sZGVycyBzaG91bGQgdGhlbiBiZSBjb3BpZWQgdG86IGByZXN1bHRzL2NlbGxfY29tbV9oZWFsdGh5L0NlbGxQaG9uZURCX3J1bnNgLiAgCk5PVEU6IGZvciBzb21lIHJlYXNvbiBJJ20gZ2V0dGluZyBhIFNlZ0ZhdWx0IHdoZW4gdHJ5aW5nIHRvIHJ1biB0aGUgaGVhdG1hcF9wbG90IGNvbW1hbmQuIFRoaXMgd2FzIHJ1biBvbiB0aGUgRXVsZXIgc2VydmVyIGluc3RlYWQuCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY29uZF9jZWxscykpewogIGNvbW0xID0gcGFzdGUwKCJjZWxscGhvbmVkYiBtZXRob2Qgc3RhdGlzdGljYWxfYW5hbHlzaXMgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICJfZXhwX25vcm0udHh0IC0tdGhyZWFkcz0xMiAtLW91dHB1dC1wYXRoPS9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9saXZlci9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIiAtLXByb2plY3QtbmFtZSAiLCBuLCAiIC0tY291bnRzLWRhdGEgZ2VuZV9uYW1lIikKICBjb21tMiA9IHBhc3RlMCgiY3AgLXIgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL2xpdmVyL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIsIG4sICIgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIpCiAgY29tbTMgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIi8iLCBuLCAiX21ldGFfbmFtZXMudHh0IHJlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyIpCiAgY29tbTQgPSBwYXN0ZTAoImNlbGxwaG9uZWRiIHBsb3QgaGVhdG1hcF9wbG90IC0tcHZhbHVlcy1wYXRoIC4vcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICIvcHZhbHVlcy50eHQgLS1vdXRwdXQtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiLyAtLWNvdW50LW5hbWUgY291bnRzX2hlYXQucGRmIC0tY291bnQtbmV0d29yay1uYW1lIGNvdW50X25ldC50eHQgLS1pbnRlcmFjdGlvbi1jb3VudC1uYW1lIGNvdW50X2ludGVyLnR4dCAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICIvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCIpCiAgCiAgcHJpbnQobikKICBwcmludChjb21tMSkKICBwcmludChjb21tMikKICBwcmludChjb21tMykKICBwcmludChjb21tNCkKICBwcmludCgiLiIpCn0KYGBgCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoY29uZF9jZWxscykpewogIGNvbW0xID0gcGFzdGUwKCJjZWxscGhvbmVkYiBtZXRob2Qgc3RhdGlzdGljYWxfYW5hbHlzaXMgL2xvY2FsMS9VU0VSUy90b21hc2dvbWVzL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvIiwgbiwgIl9tZXRhX25hbWVzLnR4dCAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iLCBuLCAiX2V4cF9ub3JtLnR4dCAtLXRocmVhZHM9MTIgLS1vdXRwdXQtcGF0aD0vbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvbGl2ZXIvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcCAtLXByb2plY3QtbmFtZSAiLCBuLCAiX3NpbXAgLS1jb3VudHMtZGF0YSBnZW5lX25hbWUiKQogIGNvbW0yID0gcGFzdGUwKCJjcCAtciAvbG9jYWwxL1VTRVJTL3RvbWFzZ29tZXMvbGl2ZXIvQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iLCBuLCAiX3NpbXAgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIpCiAgY29tbTMgPSBwYXN0ZTAoImNwIC1yIC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIl9zaW1wLyIsIG4sICJfbWV0YV9uYW1lcy50eHQgcmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0LyIsIG4sICJfc2ltcC8iKQogIGNvbW00ID0gcGFzdGUwKCJjZWxscGhvbmVkYiBwbG90IGhlYXRtYXBfcGxvdCAtLXB2YWx1ZXMtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvcHZhbHVlcy50eHQgLS1vdXRwdXQtcGF0aCAuL3Jlc3VsdHMvY2VsbF9jb21tL0NlbGxQaG9uZURCX3J1bnNfdXBkdC8iLCBuLCAiX3NpbXAvIC0tY291bnQtbmFtZSBjb3VudHNfaGVhdC5wZGYgLS1jb3VudC1uZXR3b3JrLW5hbWUgY291bnRfbmV0LnR4dCAtLWludGVyYWN0aW9uLWNvdW50LW5hbWUgY291bnRfaW50ZXIudHh0IC9sb2NhbDEvVVNFUlMvdG9tYXNnb21lcy9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgbiwgIl9zaW1wLyIsIG4sICJfbWV0YV9uYW1lcy50eHQiKQogIAogIHByaW50KG4pCiAgcHJpbnQoY29tbTEpCiAgcHJpbnQoY29tbTIpCiAgcHJpbnQoY29tbTMpCiAgcHJpbnQoY29tbTQpCiAgcHJpbnQoIi4iKQp9CmBgYAoKCgojIFByb2Nlc3MgcmVzdWx0cwpMb2FkIHJlc3VsdHMKCmBgYHtyfQpjcGRiX3BhdGggPSAicmVzdWx0cy9jZWxsX2NvbW0vQ2VsbFBob25lREJfcnVuc191cGR0IgoKc2lnX21lYW5zX25hbWVzX2wgPSBsaXN0KCkKZGVjX2wgPSBsaXN0KCkKbmV0X25hbWVzX2wgPSBsaXN0KCkKbWV0YV9sID0gbGlzdCgpCmNvbmRzID0gdW5pcXVlKGFsbGNlbGxzX2Nzc0BtZXRhLmRhdGEkQ29uZGl0aW9uKQpmb3IobiBpbiBjKGNvbmRzLCBwYXN0ZTAoY29uZHMsICJfc2ltcCIpKSl7CiAgbnMgPSBzdHJzcGxpdChuLCAiXyIpW1sxXV1bMV0KICBzaWdfbWVhbnNfbmFtZXNfbFtbbl1dID0gcmVhZC50YWJsZShwYXN0ZTAoY3BkYl9wYXRoLCAiLyIsIG4sICIvc2lnbmlmaWNhbnRfbWVhbnMudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgc2VwID0gIlx0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgZGVjX2xbW25dXSA9IHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiL2RlY29udm9sdXRlZC50eHQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCBzZXAgPSAiXHQiKQogIGNvbG5hbWVzKGRlY19sW1tuXV0pID0gZ3N1YigiLiIsICIgIiwgY29sbmFtZXMoZGVjX2xbW25dXSksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09ImdkIFQgY2VsbHMiXSA9ICJnZC1UIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTWFjcm9waGFnZXMgIEhFUzQgICJdID0gIk1hY3JvcGhhZ2VzIChIRVM0KykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJDRDggYWIgVCBjZWxscyJdID0gIkNEOCBhYi1UIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iQ0Q4IGFiIFQgY2VsbHMgMSJdID0gIkNEOCBhYi1UIGNlbGxzIDEiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJDRDggYWIgVCBjZWxscyAyIl0gPSAiQ0Q4IGFiLVQgY2VsbHMgMiIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkNEOCBhYiBUIGNlbGxzIDMiXSA9ICJDRDggYWItVCBjZWxscyAzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTmFpdmUgQ0Q0ICBUIGNlbGxzIl0gPSAiTmFpdmUgQ0Q0KyBUIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iYWIgVCBjZWxscyAgc3RyZXNzICJdID0gImFiLVQgY2VsbHMgKHN0cmVzcykiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJNb25vY3l0ZXMgIHNlY3JldG9yeSAiXSA9ICJNb25vY3l0ZXMgKHNlY3JldG9yeSkiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJNb25vY3l0ZXMgIFRSRU0yICBDRDkgICJdID0gIk1vbm9jeXRlcyAoVFJFTTIrIENEOSspIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTW9ub2N5dGVzICBJR1NGMjEgIEdQUjM0ICAiXSA9ICJNb25vY3l0ZXMgKElHU0YyMSsgR1BSMzQrKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIHN0cmVzcyAiXSA9ICJMU0VDIChzdHJlc3MpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgcmVtb2RlbGxpbmcgIl0gPSAiTFNFQyAocmVtb2RlbGxpbmcpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgaW50ZXJmZXJvbiAiXSA9ICJMU0VDIChpbnRlcmZlcm9uKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkxTRUMgIGhpZ2ggTVQgMiAiXSA9ICJMU0VDIChoaWdoIE1UIDIpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgaGlnaCBNVCAxICJdID0gIkxTRUMgKGhpZ2ggTVQgMSkiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJMU0VDICBoaWdoIE1UICJdID0gIkxTRUMgKGhpZ2ggTVQpIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iTFNFQyAgZmVuZXN0ciAgIl0gPSAiTFNFQyAoZmVuZXN0ci4pIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iS3VwZmZlciBjZWxscyAgU1VDTlIxICAiXSA9ICJLdXBmZmVyIGNlbGxzIChTVUNOUjErKSIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IklnRyAgUGxhc21hIGNlbGxzIl0gPSAiSWdHKyBQbGFzbWEgY2VsbHMiCiAgY29sbmFtZXMoZGVjX2xbW25dXSlbY29sbmFtZXMoZGVjX2xbW25dXSk9PSJJZ0EgIFBsYXNtYSBjZWxscyJdID0gIklnQSsgUGxhc21hIGNlbGxzIgogIGNvbG5hbWVzKGRlY19sW1tuXV0pW2NvbG5hbWVzKGRlY19sW1tuXV0pPT0iRUMgbm9uIExTRUMiXSA9ICJFQyBub24tTFNFQyIKICBjb2xuYW1lcyhkZWNfbFtbbl1dKVtjb2xuYW1lcyhkZWNfbFtbbl1dKT09IkRpdmlkaW5nIFQgTksgY2VsbHMiXSA9ICJEaXZpZGluZyBUL05LIGNlbGxzIgogIG5ldF9uYW1lc19sW1tuXV0gPSByZWFkLnRhYmxlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgbiwgIi9jb3VudF9uZXQudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgc2VwID0gIlx0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgbWV0YV9sW1tuXV0gPSBpZighZ3JlcGwoInNpbXAiLCBuKSl7CiAgICByZWFkLnRhYmxlKHBhc3RlMChjcGRiX3BhdGgsICIvIiwgbiwgIi8iLCBuLCAiX21ldGFfbmFtZXMudHh0IiksaGVhZGVyID0gVCxzZXAgPSAiXHQiKQogIH0gZWxzZXsKICAgIHJlYWQudGFibGUocGFzdGUwKGNwZGJfcGF0aCwgIi8iLCBuLCAiLyIsIG5zLCAiX21ldGFfbmFtZXMudHh0IiksaGVhZGVyID0gVCxzZXAgPSAiXHQiKQogIH0KfQpzYXZlUkRTKGRlY19sLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvZGVjb252b2x1dGVkX2xpc3QuUkRTIikKc2F2ZVJEUyhuZXRfbmFtZXNfbCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2NvdW50X25ldF9saXN0LlJEUyIpCmBgYAoKUmVmb3JtYXQgc2lnbmlmaWNhbnQgbWVhbnMgbWF0cml4CgpgYGB7cn0KcmVmb3JtX2xpc3QgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMobWV0YV9sKSl7CiAgc2ltcF9yZWZvcm0gPSByZXNoYXBlMjo6bWVsdChzaWdfbWVhbnNfbmFtZXNfbFtbbl1dWyxjKDE6NCw3OjksMTM6bmNvbChzaWdfbWVhbnNfbmFtZXNfbFtbbl1dKSldKQogIHNpbXBfcmVmb3JtID0gc2ltcF9yZWZvcm1bY29tcGxldGUuY2FzZXMoc2ltcF9yZWZvcm0pLF0KICAKICBscjEgPSBjKCkKICBscjIgPSBjKCkKICBmb3IoaSBpbiAxOm5yb3coc2ltcF9yZWZvcm0pKXsKICAgIGlmKGdyZXBsKCJjb21wbGV4Iiwgc2ltcF9yZWZvcm0kcGFydG5lcl9hW2ldKSl7CiAgICAgIGxyMSA9IGMobHIxLCBzdWJzdHIoc2ltcF9yZWZvcm0kcGFydG5lcl9hW2ldLCA5LDEwMCkpCiAgICB9IGVsc2V7CiAgICAgIGxyMSA9IGMobHIxLCBzdHJzcGxpdChzaW1wX3JlZm9ybSRpbnRlcmFjdGluZ19wYWlyW2ldLCAiXyIpW1sxXV1bMV0pCiAgICB9CiAgICBpZihncmVwbCgiY29tcGxleCIsIHNpbXBfcmVmb3JtJHBhcnRuZXJfYltpXSkpewogICAgICBscjIgPSBjKGxyMiwgc3Vic3RyKHNpbXBfcmVmb3JtJHBhcnRuZXJfYltpXSwgOSwxMDApKQogICAgfSBlbHNlewogICAgICBzcGx0bGVuID0gbGVuZ3RoKHN0cnNwbGl0KHNpbXBfcmVmb3JtJGludGVyYWN0aW5nX3BhaXJbaV0sICJfIilbWzFdXSkKICAgICAgbHIyID0gYyhscjIsIHN0cnNwbGl0KHNpbXBfcmVmb3JtJGludGVyYWN0aW5nX3BhaXJbaV0sICJfIilbWzFdXVtzcGx0bGVuXSkKICAgIH0KICB9CiAgc2ltcF9yZWZvcm0kbHIxID0gbHIxCiAgc2ltcF9yZWZvcm0kbHIyID0gbHIyCiAgCiAgc2ltcF9yZWZvcm0kY3QxID0gTkEKICBzaW1wX3JlZm9ybSRjdDIgPSBOQQogIGRvbmVzdCA9IHJlcChOQSwgbGVuZ3RoKHNpbXBfcmVmb3JtJGN0MSkpCiAgZG9uZWVuID0gcmVwKE5BLCBsZW5ndGgoc2ltcF9yZWZvcm0kY3QyKSkKICBmb3IoY3QgaW4gc29ydCh1bmlxdWUobWV0YV9sW1tuXV0kY2VsbF90eXBlKSwgZGVjcmVhc2luZyA9IFQpKXsKICAgIGN0X21vZCA9IGdzdWIoIi0iLCAiICIsIGN0LCBmaXhlZCA9IFQpCiAgICBjdF9tb2QgPSBnc3ViKCIrIiwgIiAiLCBjdF9tb2QsIGZpeGVkID0gVCkKICAgIGN0X21vZCA9IGdzdWIoIi8iLCAiICIsIGN0X21vZCwgZml4ZWQgPSBUKQogICAgY3RfbW9kID0gZ3N1YigiKCIsICIgIiwgY3RfbW9kLCBmaXhlZCA9IFQpCiAgICBjdF9tb2QgPSBnc3ViKCIpIiwgIiAiLCBjdF9tb2QsIGZpeGVkID0gVCkKICAgIGN0X21vZCA9IGdzdWIoIi4iLCAiICIsIGN0X21vZCwgZml4ZWQgPSBUKQogICAgc3QgPSBncmVwbChwYXN0ZTAoIl4iLGN0X21vZCwgIiAiKSwgZ3N1YigiLiIsICIgIiwgc2ltcF9yZWZvcm0kdmFyaWFibGUsIGZpeGVkID0gVCksIGZpeGVkID0gRikKICAgIGVuID0gZ3JlcGwocGFzdGUwKCIgIixjdF9tb2QsIiQiKSwgZ3N1YigiLiIsICIgIiwgc2ltcF9yZWZvcm0kdmFyaWFibGUsIGZpeGVkID0gVCksIGZpeGVkID0gRikKICAgIHNpbXBfcmVmb3JtJGN0MVtzdCAmIGlzLm5hKGRvbmVzdCldID0gY3QKICAgIHNpbXBfcmVmb3JtJGN0MltlbiAmIGlzLm5hKGRvbmVlbildID0gY3QKICAgIGRvbmVzdFtzdF0gPSBUCiAgICBkb25lZW5bZW5dID0gVAogIH0KICAjY29sbmFtZXMoZGVjX2xbW25dXSlbIShjb2xuYW1lcyhkZWNfbFtbbl1dKSAlaW4lIHVuaXF1ZShzaW1wX3JlZm9ybSRjdDIpKV0KICAKICAjIGRpcmVjdGlvbjogcmVjZXB0b3JzIGFjdGl2YXRlIGludGVybmFsIHNpZ25hbCAtIHRoYXQgaXMgdGhlIGRpcmVjdGlvbiBvZiB0aGUgc2lnbmFsbGluZwogICMjIGlmIGJvdGggYXJlIHRydWUsIHRoZSAibmV0IHNpZ25hbCIgaXMgMAogIHNpbXBfcmVmb3JtJGRpciA9IGlmZWxzZShzaW1wX3JlZm9ybSRyZWNlcHRvcl9hPT1zaW1wX3JlZm9ybSRyZWNlcHRvcl9iLCAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNpbXBfcmVmb3JtJHJlY2VwdG9yX2E9PSJUcnVlIiAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaW1wX3JlZm9ybSRyZWNlcHRvcl9iPT0iRmFsc2UiLCAtMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaW1wX3JlZm9ybSRyZWNlcHRvcl9hPT0iRmFsc2UiICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBfcmVmb3JtJHJlY2VwdG9yX2I9PSJUcnVlIiwgMSwgTkEpKSkKICAKICBzaW1wX3JlZm9ybSA9IHNpbXBfcmVmb3JtWyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsICJjdDEiLCAiY3QyIiwgImxyMSIsICJscjIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIsICJkaXIiKV0KICAKICBmb3IoaSBpbiAxOm5yb3coc2ltcF9yZWZvcm0pKXsKICAgIGlmKHNpbXBfcmVmb3JtW2ksImRpciJdPT0tMSl7CiAgICAgIHRtcCA9IHNpbXBfcmVmb3JtW2ksImN0MiJdCiAgICAgIHNpbXBfcmVmb3JtW2ksImN0MiJdID0gc2ltcF9yZWZvcm1baSwiY3QxIl0KICAgICAgc2ltcF9yZWZvcm1baSwiY3QxIl0gPSB0bXAKICAgICAgCiAgICAgIHRtcCA9IHNpbXBfcmVmb3JtW2ksImxyMiJdCiAgICAgIHNpbXBfcmVmb3JtW2ksImxyMiJdID0gc2ltcF9yZWZvcm1baSwibHIxIl0KICAgICAgc2ltcF9yZWZvcm1baSwibHIxIl0gPSB0bXAKICAgICAgCiAgICAgIHNpbXBfcmVmb3JtW2ksImRpciJdPTEKICAgIH0KICB9CiAgCiByZWZvcm1fbGlzdFtbbl1dID0gc2ltcF9yZWZvcm0KfQpzYXZlUkRTKHJlZm9ybV9saXN0LCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvcmVmb3JtX2xpc3QuUkRTIikKYGBgCgpQbG90IGludGVyYWN0aW9uIGNvdW50cwoKYGBge3J9CmludGVyX2NvdW50c19sID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKG5ldF9uYW1lc19sKSl7CiAgaW50ZXJfY291bnRzID0gcmVzaGFwZTI6OmRjYXN0KGRhdGEgPSBuZXRfbmFtZXNfbFtbbl1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXVsYSA9IFNPVVJDRX5UQVJHRVQsIHZhbHVlLnZhciA9ICJjb3VudCIpCiAgcm93bmFtZXMoaW50ZXJfY291bnRzKSA9IGludGVyX2NvdW50c1ssMV0KICBpbnRlcl9jb3VudHMgPSBpbnRlcl9jb3VudHNbLC0xXQogIAogIHBkZihwYXN0ZTAoInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvIiwgbiwgIl9udW1iZXJfb2ZfaW50ZXJhY3Rpb25zLnBkZiIpLCB1c2VEaW5nYmF0cyA9IEYsIAogICAgICBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCkKICBwaGVhdG1hcDo6cGhlYXRtYXAoaW50ZXJfY291bnRzLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwgbWFpbiA9ICJBbGwgY2x1c3RlcnMiKQogIGRldi5vZmYoKQogIGludGVyX2NvdW50c19sW1tuXV0gPSBpbnRlcl9jb3VudHMKfQpgYGAKCk1ha2luZyB0d28gY2VsbCB0eXBlIGJ5IGludGVyYWN0aW9uIG1hdHJpY2VzOiBvbmUgaGFzIHRoZSBsaWdhbmQgbWVhbiwgYW5vdGhlciB0aGUgcmVjZXB0b3IgbWVhbgoKYGBge3J9CnNjb3JpbmdfbWF0cyA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhyZWZvcm1fbGlzdCkpewogIGNsX3JlZm9ybSA9IHJlZm9ybV9saXN0W1tuXV0KICAjIGFkZCByZXZlcnNlZCBub24tZGlyZWN0ZWQgaW50ZXJhY3Rpb25zLCB0byBjb25zaWRlciB0aGVtIGluIGJvdGggZGlyZWN0aW9ucwogIGNsX3JlZm9ybTAgPSBjbF9yZWZvcm1bY2xfcmVmb3JtJGRpcj09MCxdCiAgY2xfcmVmb3JtMCA9IGNsX3JlZm9ybTBbLGMoMSwzLDIsNSw0LDYsNyldCiAgY29sbmFtZXMoY2xfcmVmb3JtMCkgPSBjb2xuYW1lcyhjbF9yZWZvcm0pCiAgY2xfcmVmb3JtID0gcmJpbmQoY2xfcmVmb3JtLCBjbF9yZWZvcm0wKQogIAogIGRlY19jbCA9IGRlY19sW1tuXV0KICAKICBtYXRfbGlnX2NsID0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSkpCiAgbWF0X3JlY19jbCA9IGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kaWRfY3BfaW50ZXJhY3Rpb24pKSwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSkpKQogIGNvbG5hbWVzKG1hdF9saWdfY2wpID0gY29sbmFtZXMobWF0X3JlY19jbCkgPSB1bmlxdWUoY2xfcmVmb3JtJGN0MSkKICByb3duYW1lcyhtYXRfbGlnX2NsKSA9IHJvd25hbWVzKG1hdF9yZWNfY2wpID0gdW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikKICBmb3IoaSBpbiAxOm5yb3coY2xfcmVmb3JtKSl7CiAgICBnMSA9IGNsX3JlZm9ybVtpLCJscjEiXQogICAgZzIgPSBjbF9yZWZvcm1baSwibHIyIl0KICAgIAogICAgYzEgPSBjbF9yZWZvcm1baSwiY3QxIl0KICAgIGMyID0gY2xfcmVmb3JtW2ksImN0MiJdCiAgICAKICAgIGludCA9IGFzLmNoYXJhY3RlcihjbF9yZWZvcm1baSwxXSkKICAgIAogICAgbTEgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssMV09PWcxLGMxXSwgbmEucm0gPSBUKQogICAgaWYoaXMubmEobTEpKSBtMSA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWyw1XT09ZzEsYzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVCkKICAgIG1hdF9saWdfY2xbaW50LCBjMV0gPSBtMQogICAgCiAgICBtMiA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWywxXT09ZzIsYzJdLCBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMikpIG0yID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDVdPT1nMixjMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMSkgfCBpcy5uYShtMikpIHByaW50KGludCkKICAgIG1hdF9yZWNfY2xbaW50LCBjMl0gPSBtMgogIH0KICAKICAjIGxpZ2FuZCB2cyByZWNlcHRvciBjb3JyZWxhdGlvbgogIGNvcl9tYXRfZWFjaF9jbCA9IGNvcihtYXRfbGlnX2NsLCBtYXRfcmVjX2NsLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJzcCIpCiAgCiAgc2NvcmluZ19tYXRzW1tuXV0gPSBsaXN0KCJtYXRfbGlnX2N0IiA9IG1hdF9saWdfY2wsICJtYXRfcmVjX2N0IiA9IG1hdF9yZWNfY2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb3JfbWF0X2VhY2giID0gY29yX21hdF9lYWNoX2NsKQogIAogICMgc3VtKGxpZywgcmVnKSBjb3JyZWxhdGlvbgogIG1hdF9saWdfY2xbaXMubmEobWF0X2xpZ19jbCldID0gMAogIG1hdF9yZWNfY2xbaXMubmEobWF0X3JlY19jbCldID0gMAogIG1hdF9ib3RoX2NsID0gbWF0X2xpZ19jbCttYXRfcmVjX2NsCiAgbWF0X2JvdGhfY2xbbWF0X2JvdGhfY2w9PTBdID0gTkEKICBjb3JfbWF0X2JvdGhfY2wgPSBjb3IobWF0X2JvdGhfY2wsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwIikKICAKICBzY29yaW5nX21hdHNbW25dXSRjb3JfbWF0X2JvdGggPSBjb3JfbWF0X2JvdGhfY2wKfQpzYXZlUkRTKHNjb3JpbmdfbWF0cywgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L3Njb3JpbmdfbWF0cy5SRFMiKQpgYGAKCk1ha2luZyB0d28gY2VsbCB0eXBlIGJ5IGludGVyYWN0aW9uIG1hdHJpY2VzOiBvbmUgaGFzIHRoZSBsaWdhbmQgbWVhbiwgYW5vdGhlciB0aGUgcmVjZXB0b3IgbWVhbiAoaGVyZSBvbmx5IGRpcmVjdGlvbmFsKQoKYGBge3J9CnNjb3JpbmdfbWF0c19kaXIgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMocmVmb3JtX2xpc3QpKXsKICBjbF9yZWZvcm0gPSByZWZvcm1fbGlzdFtbbl1dCiAgIyBhZGQgcmV2ZXJzZWQgbm9uLWRpcmVjdGVkIGludGVyYWN0aW9ucywgdG8gY29uc2lkZXIgdGhlbSBpbiBib3RoIGRpcmVjdGlvbnMKICBjbF9yZWZvcm0wID0gY2xfcmVmb3JtW2NsX3JlZm9ybSRkaXI9PTAsXQogIGNsX3JlZm9ybTAgPSBjbF9yZWZvcm0wWyxjKDEsMywyLDUsNCw2LDcpXQogIGNvbG5hbWVzKGNsX3JlZm9ybTApID0gY29sbmFtZXMoY2xfcmVmb3JtKQogIGNsX3JlZm9ybSA9IHJiaW5kKGNsX3JlZm9ybSwgY2xfcmVmb3JtMCkKICAKICBjbF9yZWZvcm0gPSBjbF9yZWZvcm1bY2xfcmVmb3JtJGRpcj09MSxdCiAgCiAgZGVjX2NsID0gZGVjX2xbW25dXQogIAogIG1hdF9saWdfY2wgPSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGlkX2NwX2ludGVyYWN0aW9uKSksIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGN0MSkpKSkKICBtYXRfcmVjX2NsID0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRpZF9jcF9pbnRlcmFjdGlvbikpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNsX3JlZm9ybSRjdDEpKSkpCiAgY29sbmFtZXMobWF0X2xpZ19jbCkgPSBjb2xuYW1lcyhtYXRfcmVjX2NsKSA9IHVuaXF1ZShjbF9yZWZvcm0kY3QxKQogIHJvd25hbWVzKG1hdF9saWdfY2wpID0gcm93bmFtZXMobWF0X3JlY19jbCkgPSB1bmlxdWUoY2xfcmVmb3JtJGlkX2NwX2ludGVyYWN0aW9uKQogIGZvcihpIGluIDE6bnJvdyhjbF9yZWZvcm0pKXsKICAgIGcxID0gY2xfcmVmb3JtW2ksImxyMSJdCiAgICBnMiA9IGNsX3JlZm9ybVtpLCJscjIiXQogICAgCiAgICBjMSA9IGNsX3JlZm9ybVtpLCJjdDEiXQogICAgYzIgPSBjbF9yZWZvcm1baSwiY3QyIl0KICAgIAogICAgaW50ID0gYXMuY2hhcmFjdGVyKGNsX3JlZm9ybVtpLDFdKQogICAgCiAgICBtMSA9IG1lYW4oZGVjX2NsW2RlY19jbCRpZF9jcF9pbnRlcmFjdGlvbj09aW50ICYgZGVjX2NsWywxXT09ZzEsYzFdLCBuYS5ybSA9IFQpCiAgICBpZihpcy5uYShtMSkpIG0xID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDVdPT1nMSxjMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUKQogICAgbWF0X2xpZ19jbFtpbnQsIGMxXSA9IG0xCiAgICAKICAgIG0yID0gbWVhbihkZWNfY2xbZGVjX2NsJGlkX2NwX2ludGVyYWN0aW9uPT1pbnQgJiBkZWNfY2xbLDFdPT1nMixjMl0sIG5hLnJtID0gVCkKICAgIGlmKGlzLm5hKG0yKSkgbTIgPSBtZWFuKGRlY19jbFtkZWNfY2wkaWRfY3BfaW50ZXJhY3Rpb249PWludCAmIGRlY19jbFssNV09PWcyLGMyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVCkKICAgIGlmKGlzLm5hKG0xKSB8IGlzLm5hKG0yKSkgcHJpbnQoaW50KQogICAgbWF0X3JlY19jbFtpbnQsIGMyXSA9IG0yCiAgfQogIAogICMgbGlnYW5kIHZzIHJlY2VwdG9yIGNvcnJlbGF0aW9uCiAgY29yX21hdF9lYWNoX2NsID0gY29yKG1hdF9saWdfY2wsIG1hdF9yZWNfY2wsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwIikKICAKICBzY29yaW5nX21hdHNfZGlyW1tuXV0gPSBsaXN0KCJtYXRfbGlnX2N0IiA9IG1hdF9saWdfY2wsICJtYXRfcmVjX2N0IiA9IG1hdF9yZWNfY2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29yX21hdF9lYWNoIiA9IGNvcl9tYXRfZWFjaF9jbCkKICAKICAjIHN1bShsaWcsIHJlZykgY29ycmVsYXRpb24KICBtYXRfbGlnX2NsW2lzLm5hKG1hdF9saWdfY2wpXSA9IDAKICBtYXRfcmVjX2NsW2lzLm5hKG1hdF9yZWNfY2wpXSA9IDAKICBtYXRfYm90aF9jbCA9IG1hdF9saWdfY2wrbWF0X3JlY19jbAogIG1hdF9ib3RoX2NsW21hdF9ib3RoX2NsPT0wXSA9IE5BCiAgY29yX21hdF9ib3RoX2NsID0gY29yKG1hdF9ib3RoX2NsLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJzcCIpCiAgCiAgc2NvcmluZ19tYXRzX2Rpcltbbl1dJGNvcl9tYXRfYm90aCA9IGNvcl9tYXRfYm90aF9jbAp9CnNhdmVSRFMoc2NvcmluZ19tYXRzX2RpciwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L3Njb3JpbmdfbWF0c19kaXIuUkRTIikKYGBgCgpDb3VudCBpbnRlcmFjdGlvbnMgb2YgZWFjaCB0eXBlCgpgYGB7cn0KaW50ZXJfY291bnRzX2RpciA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhyZWZvcm1fbGlzdCkpewogIGludGVyX2NvdW50c19kaXJbW25dXSA9IGxpc3QoKQogIGZvcihpIGluIGMoMCwxKSl7CiAgICBjbF9yZWZvcm0gPSByZWZvcm1fbGlzdFtbbl1dCiAgICBjbF9yZWZvcm0gPSBjbF9yZWZvcm1bY2xfcmVmb3JtJGRpcj09aSxdCiAgICAKICAgIG5fZGlyX2ludCA9IG1hdHJpeCgwLCBucm93ID0gbGVuZ3RoKHVuaXF1ZShjbF9yZWZvcm0kY3QxKSksCiAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aCh1bmlxdWUoY2xfcmVmb3JtJGN0MSkpKQogICAgcm93bmFtZXMobl9kaXJfaW50KSA9IGNvbG5hbWVzKG5fZGlyX2ludCkgPSB1bmlxdWUoY2xfcmVmb3JtJGN0MSkKICAgIGZvcihjMSBpbiB1bmlxdWUoY2xfcmVmb3JtJGN0MSkpewogICAgICBmb3IoYzIgaW4gdW5pcXVlKGNsX3JlZm9ybSRjdDEpKXsKICAgICAgICBuX2Rpcl9pbnRbYzEsYzJdID0gbnJvdyhjbF9yZWZvcm1bY2xfcmVmb3JtJGN0MT09YzEgJiBjbF9yZWZvcm0kY3QyPT1jMixdKQogICAgICAgIGlmKGk9PTApewogICAgICAgICAgbl9kaXJfaW50W2MxLGMyXSA9IG5fZGlyX2ludFtjMSxjMl0rbnJvdyhjbF9yZWZvcm1bY2xfcmVmb3JtJGN0MT09YzIgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbF9yZWZvcm0kY3QyPT1jMSxdKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgaW50ZXJfY291bnRzX2Rpcltbbl1dW1thcy5jaGFyYWN0ZXIoaSldXSA9IG5fZGlyX2ludAogIH0KfQpzYXZlUkRTKGludGVyX2NvdW50c19kaXIsIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9pbnRlcl9jb3VudHNfZGlyLlJEUyIpCmBgYAoKCgojIEludGVyYWN0aW9uIGFuYWx5c2lzIGluIGNvbmRpdGlvbnMKQ29tcGFyZSBpbnRlcmFjdGlvbnMgd2l0aCBERSBnZW5lcwoKYGBge3J9CiMgYWRkIGdlbmVzIGZyb20gY29tcGxleGVzCmNvbXBsZXhfcmVmID0gdW5pcXVlKHJiaW5kKGRlY19sJGhlYWx0aHlbZGVjX2wkaGVhbHRoeSRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGVtYm9saXNlZFtkZWNfbCRlbWJvbGlzZWQkaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJHJlZ2VuZXJhdGluZ1tkZWNfbCRyZWdlbmVyYXRpbmckaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGhlYWx0aHlfc2ltcFtkZWNfbCRoZWFsdGh5X3NpbXAkaXNfY29tcGxleD09IlRydWUiLGMoMSw1KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY19sJGVtYm9saXNlZF9zaW1wW2RlY19sJGVtYm9saXNlZF9zaW1wJGlzX2NvbXBsZXg9PSJUcnVlIixjKDEsNSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNfbCRyZWdlbmVyYXRpbmdfc2ltcFtkZWNfbCRyZWdlbmVyYXRpbmdfc2ltcCRpc19jb21wbGV4PT0iVHJ1ZSIsYygxLDUpXSkpCmludGVyX2RmID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHJlZm9ybV9saXN0KSl7CiAgdG1wID0gbWVyZ2UocmVmb3JtX2xpc3RbW25dXSwgY29tcGxleF9yZWYsIGJ5LnggPSA0LCBieS55ID0gMiwgYWxsLnggPSBUKVssYygyLDMsNCwxLDUsOCw2LDcpXQogIGludGVyX2RmW1tuXV0gPSBtZXJnZSh0bXAsIGNvbXBsZXhfcmVmLCBieS54ID0gNSwgYnkueSA9IDIsIGFsbC54ID0gVClbLGMoMiwzLDQsNSw2LDEsOSw3LDgpXQogIGludGVyX2RmW1tuXV0kZ2VuZV9uYW1lLnhbaXMubmEoaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueCldID0gaW50ZXJfZGZbW25dXSRscjFbaXMubmEoaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueCldCiAgaW50ZXJfZGZbW25dXSRnZW5lX25hbWUueVtpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS55KV0gPSBpbnRlcl9kZltbbl1dJGxyMltpcy5uYShpbnRlcl9kZltbbl1dJGdlbmVfbmFtZS55KV0KICBjb2xuYW1lcyhpbnRlcl9kZltbbl1dKVtjKDUsNyldID0gYygiZ24xIiwgImduMiIpCn0KCmZvcihjYyBpbiBuYW1lcyhpbnRlcl9kZikpewogICMgaW50ZXJhY3Rpb24gdW5pcXVlIGluIGNvbmRpdGlvbgogIHVuaXF1ZV9pbnRlciA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGlkX2NwX2ludGVyYWN0aW9uKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjKGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV1dXSRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmW1tuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMl1dXSRpZF9jcF9pbnRlcmFjdGlvbikpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlID0gaW50ZXJfZGZbW2NjXV0kaWRfY3BfaW50ZXJhY3Rpb24gJWluJSB1bmlxdWVfaW50ZXIKICAKICAjIGxyMSB1bmlxdWUgaW4gY29uZGl0aW9uCiAgdW5pcXVlX2xyMSA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGxyMSksCiAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoYyhpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzFdXV0kbHIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXV1dJGxyMSkpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlX2xyMSA9IGludGVyX2RmW1tjY11dJGxyMSAlaW4lIHVuaXF1ZV9scjEKICAKICAjIGxyMiB1bmlxdWUgaW4gY29uZGl0aW9uCiAgdW5pcXVlX2xyMiA9IHNldGRpZmYodW5pcXVlKGludGVyX2RmW1tjY11dJGxyMiksCiAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoYyhpbnRlcl9kZltbbmFtZXMoaW50ZXJfZGYpW25hbWVzKGludGVyX2RmKSE9Y2NdWzFdXV0kbHIyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25hbWVzKGludGVyX2RmKVtuYW1lcyhpbnRlcl9kZikhPWNjXVsyXV1dJGxyMikpKQogIGludGVyX2RmW1tjY11dJGNwZGJfdW5pcXVlX2xyMiA9IGludGVyX2RmW1tjY11dJGxyMiAlaW4lIHVuaXF1ZV9scjIKfQpmb3IoY2MgaW4gbmFtZXMoaW50ZXJfZGYpKXsKICBvYzEgPSBuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMV0KICBvYzIgPSBuYW1lcyhpbnRlcl9kZilbbmFtZXMoaW50ZXJfZGYpIT1jY11bMl0KICBpbnQxID0gcGFzdGUwKGludGVyX2RmW1tjY11dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tjY11dJGN0MSxpbnRlcl9kZltbY2NdXSRjdDIpCiAgaW50MiA9IHVuaXF1ZShwYXN0ZTAoaW50ZXJfZGZbW29jMV1dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tvYzFdXSRjdDEsaW50ZXJfZGZbW29jMV1dJGN0MikpCiAgaW50MyA9IHVuaXF1ZShwYXN0ZTAoaW50ZXJfZGZbW29jMl1dJGlkX2NwX2ludGVyYWN0aW9uLGludGVyX2RmW1tvYzJdXSRjdDEsaW50ZXJfZGZbW29jMl1dJGN0MikpCiAgdW5pcXVlX2ludGVyID0gc2V0ZGlmZih1bmlxdWUoaW50MSksIHVuaXF1ZShjKGludDIsIGludDMpKSkKICBpbnRlcl9kZltbY2NdXSRjcGRiX3VuaXF1ZV9jdCA9IGludDEgJWluJSB1bmlxdWVfaW50ZXIKfQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZikpewogIGRmID0gaW50ZXJfZGZbW25dXQogIGRmID0gdW5pcXVlKGRmWyxjKDE6MyldKQogIAogIGludHNfdW5fY3QgPSByb3dTdW1zKHRhYmxlKGRmJGlkX2NwX2ludGVyYWN0aW9uLCBkZiRjdDEpPjApPDQgfCAKICAgIHJvd1N1bXModGFibGUoZGYkaWRfY3BfaW50ZXJhY3Rpb24sIGRmJGN0Mik+MCk8NAogIAogIGludGVyX2RmW1tuXV0kaW50ZXJfY3Rfc3BlYyA9IGludGVyX2RmW1tuXV0kaWRfY3BfaW50ZXJhY3Rpb24gJWluJSBuYW1lcyhpbnRzX3VuX2N0KVtpbnRzX3VuX2N0XQp9CmZvcihuIGluIG5hbWVzKGludGVyX2RmKSl7CiAgeHh4ID0gZGF0YS5mcmFtZShjdCA9IGMoaW50ZXJfZGZbW25dXVssMl0sIGludGVyX2RmW1tuXV1bLDNdKSwgCiAgICAgICAgICAgICAgICAgICBsciA9IGMoaW50ZXJfZGZbW25dXVssNF0sIGludGVyX2RmW1tuXV1bLDZdKSkKICB4eHggPSB1bmlxdWUoeHh4KQogIHRhYl9sciA9ICh0YWJsZSh4eHgkbHIsIHh4eCRjdCk+MCkqMQogIHRhYl9sciA9IHJvd1N1bXModGFiX2xyKQogIAogIGludGVyX2RmW1tuXV0kbHIxX25jdCA9IHRhYl9scltpbnRlcl9kZltbbl1dJGxyMV0KICBpbnRlcl9kZltbbl1dJGxyMl9uY3QgPSB0YWJfbHJbaW50ZXJfZGZbW25dXSRscjJdCn0Kc2F2ZVJEUyhpbnRlcl9kZiwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2NvbmRfZGlmZl9pbnRlcmFjdF9ERS5SRFMiKQpgYGAKClBsb3QgaW50ZXJhY3Rpb24gaGVhdG1hcCAtIHRvdGFsIGFuZCBmaWx0ZXJlZAoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KaW50X2NvdW50c190b3RhbCA9IGxpc3QoKQppbnRfY291bnRzX2ZpbHQgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoaW50ZXJfZGYpKXsKICBzdWJkZmN0ID0gdW5pcXVlKGludGVyX2RmW1tuXV1bLDE6M10pCiAgc3ViZGZjdCA9IHVuaXF1ZShzdWJkZmN0KQogIGRmY3QgPSBkYXRhLmZyYW1lKCJjdDEiID0gYyhzdWJkZmN0JGN0MSwgc3ViZGZjdCRjdDIpLAogICAgICAgICAgICAgICAgICAgICJjdDIiID0gYyhzdWJkZmN0JGN0Miwgc3ViZGZjdCRjdDEpKQogIGludF9jb3VudHNfdG90YWxbW25dXSA9IGRmY3QKICBwaGVhdG1hcDo6cGhlYXRtYXAodGFibGUoZGZjdCRjdDEsIGRmY3QkY3QyKSwgbWFpbiA9IG4pCiAgCiAga2VlcCA9IGludGVyX2RmW1tuXV0kbHIxX25jdDw9MSB8IGludGVyX2RmW1tuXV0kbHIyX25jdDw9MQogIHN1YmRmY3QgPSB1bmlxdWUoaW50ZXJfZGZbW25dXVtrZWVwLGMoMTozLCAxNSwxNildKQogIHN3cCA9IHN1YmRmY3RbLDVdPT0xCiAgdG1wID0gc3ViZGZjdCRjdDJbc3dwXQogIHN1YmRmY3QkY3QyW3N3cF0gPSBzdWJkZmN0JGN0MVtzd3BdCiAgc3ViZGZjdCRjdDFbc3dwXSA9IHRtcAogIHRtcCA9IHN1YmRmY3QkbHIyX25jdFtzd3BdCiAgc3ViZGZjdCRscjJfbmN0W3N3cF0gPSBzdWJkZmN0JGxyMV9uY3Rbc3dwXQogIHN1YmRmY3QkbHIxX25jdFtzd3BdID0gdG1wCiAgZHVwID0gc3ViZGZjdFtzdWJkZmN0JGxyMV9uY3Q9PTEgJiBzdWJkZmN0JGxyMl9uY3Q9PTEsXQogIHRtcCA9IGR1cCRjdDIKICBkdXAkY3QyID0gZHVwJGN0MQogIGR1cCRjdDEgPSB0bXAKICBzdWJkZmN0ID0gcmJpbmQoc3ViZGZjdCwgZHVwKQogIHN1YmRmY3QgPSB1bmlxdWUoc3ViZGZjdFssMTozXSkKICBpbnRfY291bnRzX2ZpbHRbW25dXSA9IGRmY3QKICBwaGVhdG1hcDo6cGhlYXRtYXAodGFibGUoc3ViZGZjdCRjdDEsIHN1YmRmY3QkY3QyKSwgbWFpbiA9IG4pCn0Kc2F2ZVJEUyhpbnRfY291bnRzX3RvdGFsLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvaW50X2NvdW50c190b3RhbC5SRFMiKQpzYXZlUkRTKGludF9jb3VudHNfZmlsdCwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L2ludF9jb3VudHNfZmlsdC5SRFMiKQpgYGAKCkdldCBub24tdWJpcXVpdG91cyBpbnRlcmFjdGlvbnMKCmBgYHtyfQppbnRlcl9ub25zcyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbmVtYiA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbikpCmludGVyX25vbnJlZyA9IHNldGRpZmYodW5pcXVlKGMoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpCgpjdF9nX2NvbmQgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoaW50ZXJfZGYpWzE6M10pewogIGRmID0gaW50ZXJfZGZbW25dXVtpbnRlcl9kZltbbl1dJGNwZGJfdW5pcXVlIHwgCiAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGZbW25dXSRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGMoaW50ZXJfbm9uc3MsIGludGVyX25vbmVtYiwgaW50ZXJfbm9ucmVnKSxdCiAgI2RmID0gZGZbZGYkbHIxX25jdDw9MSB8IGRmJGxyMl9uY3Q8PTEsXQogIAogIGN0cyA9IHVuaXF1ZShjKGRmJGN0MSwgZGYkY3QyKSkKICBjdF9nX2xpc3QgPSBsaXN0KCkKICBmb3IoY3QgaW4gY3RzKXsKICAgIGN0X2dfbGlzdFtbY3RdXSA9IGRhdGEuZnJhbWUoImludGVyYWN0IiA9IGMoYXMuY2hhcmFjdGVyKGRmJGlkX2NwX2ludGVyYWN0aW9uW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGlkX2NwX2ludGVyYWN0aW9uW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lIiA9IGMoYXMuY2hhcmFjdGVyKGRmJGduMVtkZiRjdDE9PWN0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGduMltkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV90YXJnZXQiID0gYyhhcy5jaGFyYWN0ZXIoZGYkZ24yW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGduMVtkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY3QiID0gY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdF90YXJnZXQiID0gYyhhcy5jaGFyYWN0ZXIoZGYkY3QyW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGRmJGN0MVtkZiRjdDI9PWN0XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZCIgPSBuLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICB9CiAgY3RfZ19jb25kW1tuXV0gPSB1bmlxdWUoUmVkdWNlKHJiaW5kLCBjdF9nX2xpc3QpKQp9CmN0X2dfY29uZCA9IFJlZHVjZShyYmluZCwgY3RfZ19jb25kKQpkaW0oY3RfZ19jb25kKQpsZW5ndGgodW5pcXVlKGN0X2dfY29uZCRpbnRlcmFjdCkpCgoKaW50ZXJfbm9uc3MgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRoZWFsdGh5X3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQppbnRlcl9ub25lbWIgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGhlYWx0aHlfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyX2RmJHJlZ2VuZXJhdGluZ19zaW1wJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkZW1ib2xpc2VkX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQppbnRlcl9ub25yZWcgPSBzZXRkaWZmKHVuaXF1ZShjKGludGVyX2RmJGVtYm9saXNlZF9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJfZGYkaGVhbHRoeV9zaW1wJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoaW50ZXJfZGYkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKQoKY3RzX2dfY29uZCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhpbnRlcl9kZilbNDo2XSl7CiAgZGYgPSBpbnRlcl9kZltbbl1dW2ludGVyX2RmW1tuXV0kY3BkYl91bmlxdWUgfCAKICAgICAgICAgICAgICAgICAgICAgICBpbnRlcl9kZltbbl1dJGlkX2NwX2ludGVyYWN0aW9uICVpbiUgYyhpbnRlcl9ub25zcywgaW50ZXJfbm9uZW1iLCBpbnRlcl9ub25yZWcpLF0KICAjZGYgPSBkZltkZiRscjFfbmN0PD0xIHwgZGYkbHIyX25jdDw9MSxdCiAgCiAgY3RzID0gdW5pcXVlKGMoZGYkY3QxLCBkZiRjdDIpKQogIGN0X2dfbGlzdCA9IGxpc3QoKQogIGZvcihjdCBpbiBjdHMpewogICAgY3RfZ19saXN0W1tjdF1dID0gZGF0YS5mcmFtZSgiaW50ZXJhY3QiID0gYyhhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkaWRfY3BfaW50ZXJhY3Rpb25bZGYkY3QyPT1jdF0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmUiID0gYyhhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0MT09Y3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24yW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRnbjJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkZ24xW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdCIgPSBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImN0X3RhcmdldCIgPSBjKGFzLmNoYXJhY3RlcihkZiRjdDJbZGYkY3QxPT1jdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoZGYkY3QxW2RmJGN0Mj09Y3RdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb25kIiA9IG4sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIH0KICBjdHNfZ19jb25kW1tuXV0gPSB1bmlxdWUoUmVkdWNlKHJiaW5kLCBjdF9nX2xpc3QpKQp9CmN0c19nX2NvbmQgPSBSZWR1Y2UocmJpbmQsIGN0c19nX2NvbmQpCmRpbShjdHNfZ19jb25kKQpsZW5ndGgodW5pcXVlKGN0c19nX2NvbmQkaW50ZXJhY3QpKQoKY3RfZ19sID0gbGlzdCgiY3RzX2dfY29uZCIgPSBjdHNfZ19jb25kLAogICAgICAgICAgICAgICJjdF9nX2NvbmQiID0gY3RfZ19jb25kKQpgYGAKCkFubm90YXRlIGludGVyYWN0aW9ucwoKYGBge3J9CmludGVyX2Fubm90ID0gcmVhZC5jc3YoInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvaW50ZXJfdW5pcXVlNS5jc3YiLCBoZWFkZXIgPSBULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKI3h4eCA9IG1lcmdlKHVuaXF1ZShyYmluZChjdF9nX2NvbmRbLDE6M10sIGN0c19nX2NvbmRbLDE6M10pKSwgCiMgICAgICAgICAgICB1bmlxdWUoaW50ZXJfYW5ub3RbLGMoMSw0KV0pLCBieSA9IDEsIGFsbC54ID0gVCkKI3dyaXRlLmNzdih4eHgsIGZpbGUgPSAiaW50ZXJfdW5pcXVlNS5jc3YiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCgppbnRlcl9hbm5vdCA9IHVuaXF1ZShpbnRlcl9hbm5vdFssYygxLDQpXSkKCmRlc2NyaXB0aW9uID0gc3Ryc3BsaXQoaW50ZXJfYW5ub3QkZGVzY3JpcHRpb24sICI7IikKaW50ZXJfZGVzID0gbGFwcGx5KDE6bGVuZ3RoKGRlc2NyaXB0aW9uKSwgCiAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSByZXAoaW50ZXJfYW5ub3QkaW50ZXJhY3RbeF0sIGxlbmd0aChkZXNjcmlwdGlvbltbeF1dKSkpCmludGVyX2Fubm90ID0gZGF0YS5mcmFtZSgiaW50ZXIiID0gdW5saXN0KGludGVyX2RlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAiZnVuY3QiID0gdW5saXN0KGRlc2NyaXB0aW9uKSkKCmludGVyX2Fubm90JGZ1bmN0W2ludGVyX2Fubm90JGZ1bmN0PT0iaW50ZXJjZWxsdWxhciBhZGhlc2lvbiJdID0gImFkaGVzaW9uIgppbnRlcl9hbm5vdCRmdW5jdFtpbnRlcl9hbm5vdCRmdW5jdD09ImFudGlib2R5IHJlZ3VsYXRpb24iXSA9ICJpbW11bmUgcmVndWxhdGlvbiIKaW50ZXJfYW5ub3QkZnVuY3RbaW50ZXJfYW5ub3QkZnVuY3Q9PSJhbnRpZ2VuIHByZXNlbnRhdGlvbiJdID0gImltbXVuZSBhY3Rpdml0eSIKCndyaXRlLmNzdihpbnRlcl9hbm5vdCwgZmlsZSA9ICJkYXRhL2ludGVyYWN0aW9uX2Fubm90YXRpb24yLmNzdiIsIAogICAgICAgICAgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCwgcXVvdGUgPSBGKQpjb2xuYW1lcyhpbnRlcl9hbm5vdCkgPSBjKCJpbnRlcmFjdCIsICJkZXNjcmlwdGlvbiIpCgpjdF9nX2NvbmRfYW5uX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoY3RfZ19sKSl7CiAgY3RfZ19jb25kX2FubiA9IG1lcmdlKGN0X2dfbFtbbl1dLCBpbnRlcl9hbm5vdCwgYnkgPSAxLCBhbGwueCA9IFQpCiAgY3RfZ19jb25kX2FubiA9IHVuaXF1ZShjdF9nX2NvbmRfYW5uWyxjKDEsNCw1LDYsNyldKQogIAogIGN0X2dfY29uZF9hbm4gPSBkYXRhLmZyYW1lKCJpbnRlcmFjdGlvbnMiID0gcmVwKGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGludGVyYWN0KSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2VsbHR5cGVzIiA9IGMoYXMuY2hhcmFjdGVyKGN0X2dfY29uZF9hbm4kY3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoY3RfZ19jb25kX2FubiRjdF90YXJnZXQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA9IHJlcChhcy5jaGFyYWN0ZXIoY3RfZ19jb25kX2FubiRjb25kKSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iID0gcmVwKGFzLmNoYXJhY3RlcihjdF9nX2NvbmRfYW5uJGRlc2NyaXB0aW9uKSwyKSkKICBjdF9nX2NvbmRfYW5uJGNvbmRpdGlvbiA9IGZhY3Rvcih1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGN0X2dfY29uZF9hbm4kY29uZGl0aW9uLCAiXyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHhbMV0pKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiaGVhbHRoeSIsICJlbWJvbGlzZWQiLCAicmVnZW5lcmF0aW5nIikpCiAgCiAgcGx0X2Jhcl9mdW5jID0gZ2dwbG90KGN0X2dfY29uZF9hbm4sIGFlcyh4ID0gY29uZGl0aW9uLCBmaWxsID0gY29uZGl0aW9uKSkrCiAgICBmYWNldF9ncmlkKGRlc2NyaXB0aW9ufmNlbGx0eXBlcywgc2NhbGVzID0gImZyZWVfeSIpKwogICAgZ2VvbV9iYXIoKSsKICAgIHRoZW1lX2J3KCkrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgcGRmKHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC8iLCBuLCAiX3BsdF9iYXJfZnVuYy5wZGYiKSwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gNTApCiAgcHJpbnQocGx0X2Jhcl9mdW5jKQogIGRldi5vZmYoKQogIAogIGN0X2dfY29uZF9hbm5fbFtbbl1dID0gY3RfZ19jb25kX2FubgogIHNhdmVSRFMoY3RfZ19jb25kX2FubiwgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC8iLCBuLCAiX2Fubi5SRFMiKSkKfQpgYGAKCkdldCBleHByZXNzaW9uIGZvciBlYWNoIGludGVyYWN0aW9uIGluIGVhY2ggY29uZGl0aW9uCgpgYGB7cn0KZXhwX2xpc3QgPSBsaXN0KCkKaW50X2dyb3VwcyA9IGxpc3QoImN0c19nX2NvbmQiID0gdW5pcXVlKGMoc2lnX21lYW5zX25hbWVzX2wkaGVhbHRoeV9zaW1wJGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdfbWVhbnNfbmFtZXNfbCRlbWJvbGlzZWRfc2ltcCRpZF9jcF9pbnRlcmFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnX21lYW5zX25hbWVzX2wkcmVnZW5lcmF0aW5nX3NpbXAkaWRfY3BfaW50ZXJhY3Rpb24pKSwKICAgICAgICAgICAgICAgICAgImN0X2dfY29uZCIgPSB1bmlxdWUoYyhzaWdfbWVhbnNfbmFtZXNfbCRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdfbWVhbnNfbmFtZXNfbCRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ19tZWFuc19uYW1lc19sJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpKQpmb3IobiBpbiBuYW1lcyhzaWdfbWVhbnNfbmFtZXNfbCkpewogIGRmID0gc2lnX21lYW5zX25hbWVzX2xbW25dXVssYygxLDU6NiwxMzpuY29sKHNpZ19tZWFuc19uYW1lc19sW1tuXV0pKV0KICAKICAjY29sbmFtZXMoZGYpID0gZ3N1YigiLiIsICIgIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiZ2QuVC5jZWxscyIsICJnZC1UIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiQi5jZWxscyIsICJCIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRGl2aWRpbmcuZW5kb3RoZWxpYWwuY2VsbHMiLCAiRGl2aWRpbmcgZW5kb3RoZWxpYWwgY2VsbHMiLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkRpdmlkaW5nLmNEQ3MiLCAiRGl2aWRpbmcgY0RDcyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkluZmlsdHJhdGluZy5OSy5jZWxscyIsICJJbmZpbHRyYXRpbmcgTksgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNYWNyb3BoYWdlcy4uSEVTNC4uIiwgIk1hY3JvcGhhZ2VzIChIRVM0KykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJhY3RpdmF0ZWQuRENzIiwgImFjdGl2YXRlZCBEQ3MiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJQZXJpY2VudHJhbC5MU0VDIiwgIlBlcmljZW50cmFsIExTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNaWR6b25hbC5MU0VDIiwgIk1pZHpvbmFsIExTRUMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJQZXJpcG9ydGFsLkxTRUMiLCAiUGVyaXBvcnRhbCBMU0VDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiUGVyaXBvcnRhbC5MU0VDIiwgIlBlcmlwb3J0YWwgTFNFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5LLmNlbGxzLjEiLCAiTksgY2VsbHMgMSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5LLmNlbGxzLjIiLCAiTksgY2VsbHMgMiIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5LLmNlbGxzLjMiLCAiTksgY2VsbHMgMyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk5LLmNlbGxzIiwgIk5LIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiQ0Q4LmFiLlQuY2VsbHMuMSIsICJDRDggYWItVCBjZWxscyAxIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiQ0Q4LmFiLlQuY2VsbHMuMiIsICJDRDggYWItVCBjZWxscyAyIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiQ0Q4LmFiLlQuY2VsbHMuMyIsICJDRDggYWItVCBjZWxscyAzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiQ0Q4LmFiLlQuY2VsbHMiLCAiQ0Q4IGFiLVQgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJOYWl2ZS5DRDQuLlQuY2VsbHMiLCAiTmFpdmUgQ0Q0KyBUIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiYWIuVC5jZWxscy4uc3RyZXNzLiIsICJhYi1UIGNlbGxzIChzdHJlc3MpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTW9ub2N5dGVzLi5zZWNyZXRvcnkuIiwgIk1vbm9jeXRlcyAoc2VjcmV0b3J5KSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1vbm9jeXRlcy4uVFJFTTIuLkNEOS4uIiwgIk1vbm9jeXRlcyAoVFJFTTIrIENEOSspIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTW9ub2N5dGVzLi5JR1NGMjEuLkdQUjM0Li4iLCAiTW9ub2N5dGVzIChJR1NGMjErIEdQUjM0KykiLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLnN0cmVzcy4iLCAiTFNFQyAoc3RyZXNzKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLnJlbW9kZWxsaW5nLiIsICJMU0VDIChyZW1vZGVsbGluZykiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJMU0VDLi5pbnRlcmZlcm9uLiIsICJMU0VDIChpbnRlcmZlcm9uKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLmhpZ2guTVQuMi4iLCAiTFNFQyAoaGlnaCBNVCAyKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLmhpZ2guTVQuMS4iLCAiTFNFQyAoaGlnaCBNVCAxKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkxTRUMuLmhpZ2guTVQuIiwgIkxTRUMgKGhpZ2ggTVQpIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTFNFQy4uZmVuZXN0ci4uIiwgIkxTRUMgKGZlbmVzdHIuKSIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkt1cGZmZXIuY2VsbHMuLlNVQ05SMS4uIiwgIkt1cGZmZXIgY2VsbHMgKFNVQ05SMSspIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiSWdHLi5QbGFzbWEuY2VsbHMiLCAiSWdHKyBQbGFzbWEgY2VsbHMiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJJZ0EuLlBsYXNtYS5jZWxscyIsICJJZ0ErIFBsYXNtYSBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIkVDLm5vbi5MU0VDIiwgIkVDIG5vbi1MU0VDIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiRGl2aWRpbmcuVC5OSy5jZWxscyIsICJEaXZpZGluZyBUL05LIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiS3VwZmZlci5jZWxscyIsICJLdXBmZmVyIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiVFJNLmNlbGxzIiwgIlRSTSBjZWxscyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIk1BSVQuY2VsbHMuMSIsICJNQUlUIGNlbGxzIDEiLCBjb2xuYW1lcyhkZiksIGZpeGVkID0gVCkKICBjb2xuYW1lcyhkZikgPSBnc3ViKCJNQUlULmNlbGxzLjIiLCAiTUFJVCBjZWxscyAyIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTUFJVC5jZWxscyIsICJNQUlUIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgY29sbmFtZXMoZGYpID0gZ3N1YigiTHltcGhhdGljLkVDIiwgIkx5bXBoYXRpYyBFQyIsIGNvbG5hbWVzKGRmKSwgZml4ZWQgPSBUKQogIGNvbG5hbWVzKGRmKSA9IGdzdWIoIlN0ZWxsYXRlLmNlbGxzIiwgIlN0ZWxsYXRlIGNlbGxzIiwgY29sbmFtZXMoZGYpLCBmaXhlZCA9IFQpCiAgIyBoZWFkKGNvbG5hbWVzKGRmKSwgNjApCiAgCiAgZGYgPSByZXNoYXBlMjo6bWVsdChkZiwgaWQudmFycyA9IDE6MykKICBkZiR2YWx1ZVtpcy5uYShkZiR2YWx1ZSldID0gMAogIGRmID0gZGYgJT4lCiAgIGdyb3VwX2J5KGlkX2NwX2ludGVyYWN0aW9uLCBnZW5lX2EsIGdlbmVfYiwgdmFyaWFibGUpICU+JQogICBzdW1tYXJpc2UodmFsdWU9KG1lYW4odmFsdWUpKSwgLmdyb3VwcyA9ICJrZWVwIikKICBkZiA9IGFzLmRhdGEuZnJhbWUoZGYpCiAgCiAgbnMgPSBpZihncmVwbCgic2ltcCIsIG4pKSAiY3RzX2dfY29uZCIgZWxzZSAiY3RfZ19jb25kIgogIGN0X2dfY29uZF9hbm4gPSBjdF9nX2NvbmRfYW5uX2xbW25zXV0KICBzdWJfZGYgPSBkZltkZiRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGN0X2dfY29uZF9hbm4kaW50ZXJhY3Rpb25zLF0KICBzdWJfZGYgPSBtZXJnZShjdF9nX2xbW25zXV0sIHN1Yl9kZiwgYWxsID0gVCwgYnkgPSAxKQogIHN1Yl9kZiA9IHN1Yl9kZltzdWJfZGYkaW50ZXJhY3QgJWluJSBpbnRfZ3JvdXBzW1tuc11dLF0KICBzdWJfZGYkdmFyaWFibGUgPSBhcy5jaGFyYWN0ZXIoc3ViX2RmJHZhcmlhYmxlKQogIHN1Yl9kZiR2YWx1ZVtpcy5uYShzdWJfZGYkdmFsdWUpXSA9IDAKICAKICBjdDEgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHN1Yl9kZiR2YXJpYWJsZSwgIi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4WzFdKSkKICBjdDIgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHN1Yl9kZiR2YXJpYWJsZSwgIi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4WzJdKSkKICBrZWVwID0gc2FwcGx5KDE6bnJvdyhzdWJfZGYpLCBmdW5jdGlvbih4KSAoc3ViX2RmJGN0W3hdPT1jdDFbeF0gJiBzdWJfZGYkY3RfdGFyZ2V0W3hdPT1jdDJbeF0pIHwKICAgICAgICAgICAgICAgICAgKHN1Yl9kZiRjdFt4XT09Y3QyW3hdICYgc3ViX2RmJGN0X3RhcmdldFt4XT09Y3QxW3hdKSkKICBleHBfbGlzdFtbbl1dID0gc3ViX2RmW2tlZXAsYygxOjYsMTApXQp9CmZvcihuIGluIG5hbWVzKGV4cF9saXN0KSl7CiAgZXhwX2xpc3RbW25dXSA9IGV4cF9saXN0W1tuXV1bIWlzLm5hKGV4cF9saXN0W1tuXV0kaW50ZXJhY3QpLF0KfQoKY3RfaW50X2V4cF9sID0gbGlzdCgpCmZvcihzaW1wIGluIGMoVCwgRikpewogIG5fdXNlID0gbmFtZXMoZXhwX2xpc3QpW2dyZXBsKCJzaW1wIiwgbmFtZXMoZXhwX2xpc3QpKT09c2ltcF0KICAjY3RfaW50X2V4cCA9IGNiaW5kKGV4cF9saXN0W1tuX3VzZVsxXV1dLCBleHBfbGlzdFtbbl91c2VbM11dXSR2YWx1ZSwgZXhwX2xpc3RbW25fdXNlWzNdXV0kdmFsdWUpCiAgY3RfaW50X2V4cCA9IG1lcmdlKGV4cF9saXN0W1tuX3VzZVsxXV1dLCBleHBfbGlzdFtbbl91c2VbMl1dXSwgYnkgPSAxOjYpCiAgY3RfaW50X2V4cCA9IG1lcmdlKGN0X2ludF9leHAsIGV4cF9saXN0W1tuX3VzZVszXV1dLCBieSA9IDE6NikKICBjdF9pbnRfZXhwJHZhbHVlLnhbaXMubmEoY3RfaW50X2V4cCR2YWx1ZS54KV0gPSBjdF9pbnRfZXhwJHZhbHVlLnlbaXMubmEoY3RfaW50X2V4cCR2YWx1ZS55KV0gPSBjdF9pbnRfZXhwJHZhbHVlW2lzLm5hKGN0X2ludF9leHAkdmFsdWUpXSA9IDAKICBjb2xuYW1lcyhjdF9pbnRfZXhwKVs3OjldID0gYygiaGVhbHRoeV9leHAiLCAiZW1ib2xpc2VkX2V4cCIsICJyZWdlbmVyYXRpbmdfZXhwIikKICBjdF9pbnRfZXhwID0gY3RfaW50X2V4cFtjdF9pbnRfZXhwJGhlYWx0aHlfZXhwPjAgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0X2ludF9leHAkZW1ib2xpc2VkX2V4cD4wIHwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdF9pbnRfZXhwJHJlZ2VuZXJhdGluZ19leHA+MCxdCgogIHR1cF9saXN0ID0gbGlzdCgpCiAga2VlcF9yb3cgPSBjKCkKICBmb3IoaSBpbiAxOm5yb3coY3RfaW50X2V4cCkpewogICAgdHVwMSA9IHBhc3RlKGMoY3RfaW50X2V4cCRnZW5lW2ldLCBjdF9pbnRfZXhwJGdlbmVfdGFyZ2V0W2ldLCBjdF9pbnRfZXhwJGN0W2ldLCAKICAgICAgICAgICAgICAgICAgIGN0X2ludF9leHAkY3RfdGFyZ2V0W2ldLCBjdF9pbnRfZXhwJGNvbmRbaV0pLCBjb2xsYXBzZSA9ICIgIikKICAgIHR1cDIgPSBwYXN0ZShjKGN0X2ludF9leHAkZ2VuZV90YXJnZXRbaV0sIGN0X2ludF9leHAkZ2VuZVtpXSwgY3RfaW50X2V4cCRjdF90YXJnZXRbaV0sIAogICAgICAgICAgICAgICAgICAgY3RfaW50X2V4cCRjdFtpXSwgY3RfaW50X2V4cCRjb25kW2ldKSwgY29sbGFwc2UgPSAiICIpCiAgICBpZighKHR1cDEgJWluJSB0dXBfbGlzdCkgJiAhKHR1cDIgJWluJSB0dXBfbGlzdCkpewogICAgICB0dXBfbGlzdCA9IGModHVwX2xpc3QsIHR1cDEsIHR1cDIpCiAgICAgIGtlZXBfcm93ID0gYyhrZWVwX3JvdywgVCkKICAgIH0gZWxzZXsKICAgICAga2VlcF9yb3cgPSBjKGtlZXBfcm93LCBGKQogICAgfQogIH0KICBjdF9pbnRfZXhwID0gY3RfaW50X2V4cFtrZWVwX3JvdyxdCiAgCiAgY3RfaW50X2V4cCA9IG1lcmdlKGN0X2ludF9leHAsIHVuaXF1ZShjdF9nX2NvbmRfYW5uWyxjKDEsNCldKSwgYnkgPSAxLCBhbGwgPSBUKQogIG5uID0gaWYoc2ltcCkgInNpbXAiIGVsc2UgImFsbCIKICBjdF9pbnRfZXhwX2xbW25uXV0gPSBjdF9pbnRfZXhwCiAgY3RfaW50X2V4cF9sW1tubl1dID0gY3RfaW50X2V4cF9sW1tubl1dW2NvbXBsZXRlLmNhc2VzKGN0X2ludF9leHBfbFtbbm5dXSksXQogIAogIGN0X2ludF9leHBfbFtbbm5dXSRjb25kID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChjdF9pbnRfZXhwX2xbW25uXV0kY29uZCwgIl8iKSwgZnVuY3Rpb24oeCkgeFsxXSkpCn0KCnNhdmVSRFMoY3RfaW50X2V4cF9sLCBmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvaW50ZXJhY3RfY2VsbHR5cGVfZXhwX2dyb3VwX2xpc3QuUkRTIikKYGBgCgpWYXJpYWJpbGl0eSBvZiBpbnRlcmFjdGlvbnMKCmBgYHtyfQppbnRlcl9hbm5vdCA9IHJlYWQuY3N2KCJkYXRhL2ludGVyYWN0aW9uX2Fubm90YXRpb24yLmNzdiIsIGhlYWRlciA9IFQpCmludGVyX2Fubm90JGZ1bmN0W2dyZXBsKCJpbW0iLCBpbnRlcl9hbm5vdCRmdW5jdCldID0gImltbXVuZSIKaW50ZXJfYW5ub3QkZnVuY3RbZ3JlcGwoImluZmxhbSIsIGludGVyX2Fubm90JGZ1bmN0KV0gPSAiaW1tdW5lIgpnciA9IGludGVyX2Fubm90JGZ1bmN0Cm5hbWVzKGdyKSA9IGludGVyX2Fubm90JGludGVyCmdyX2xpc3QgPSB0YXBwbHkoaW50ZXJfYW5ub3QkaW50ZXIsIGludGVyX2Fubm90JGZ1bmN0LCBmdW5jdGlvbih4KSB4KQoKaGVyX2FsbGludF9sID0gbGlzdCgpCmZvcihzaW1wIGluIGMoVCwgRikpewogIG5fdXNlID0gbmFtZXMocmVmb3JtX2xpc3QpW2dyZXBsKCJzaW1wIiwgbmFtZXMocmVmb3JtX2xpc3QpKT09c2ltcF0KICAKICBoZV9hbGxpbnQgPSBtZXJnZShyZWZvcm1fbGlzdFtbbl91c2VbMV1dXVssMTo2XSwgcmVmb3JtX2xpc3RbW25fdXNlWzJdXV1bLDE6Nl0sIGJ5ID0gMTozLCBhbGwgPSBUKQogIGhlX2FsbGludCRscjEueFtpcy5uYShoZV9hbGxpbnQkbHIxLngpXSA9IGhlX2FsbGludCRscjEueVtpcy5uYShoZV9hbGxpbnQkbHIxLngpXQogIGhlX2FsbGludCRscjIueFtpcy5uYShoZV9hbGxpbnQkbHIyLngpXSA9IGhlX2FsbGludCRscjIueVtpcy5uYShoZV9hbGxpbnQkbHIyLngpXQogIGhlX2FsbGludCA9IGhlX2FsbGludFssYygxOjYsOSldCiAgaGVyX2FsbGludCA9IG1lcmdlKGhlX2FsbGludCwgcmVmb3JtX2xpc3RbW25fdXNlWzNdXV1bLDE6Nl0sIGJ5ID0gMTozLCBhbGwgPSBUKQogIGhlcl9hbGxpbnQkbHIxLnhbaXMubmEoaGVyX2FsbGludCRscjEueCldID0gaGVyX2FsbGludCRscjFbaXMubmEoaGVyX2FsbGludCRscjEueCldCiAgaGVyX2FsbGludCRscjIueFtpcy5uYShoZXJfYWxsaW50JGxyMi54KV0gPSBoZXJfYWxsaW50JGxyMltpcy5uYShoZXJfYWxsaW50JGxyMi54KV0KICBoZXJfYWxsaW50ID0gaGVyX2FsbGludFssYygxOjcsMTApXQogIGhlcl9hbGxpbnRbaXMubmEoaGVyX2FsbGludCldID0gMAogIGNvbG5hbWVzKGhlcl9hbGxpbnQpWzQ6OF0gPSBjKCJscjEiLCAibHIyIiwgImhlYWx0aHlfZXhwIiwgImVtYm9saXNlZF9leHAiLCAicmVnZW5lcmF0aW5nX2V4cCIpCiAgCiAgIyBjb3VudCBvY2N1cnJlbmNlcyBwZXIgY29uZGl0aW9uLiB0aGlzIHdvcmtzIGJjIHdlJ3JlIGFscmVhZHkgd29ya2luZyB3aXRoIHNpZyBtZWFucwogIGhlcl9hbGxpbnQgPSBtZXJnZShoZXJfYWxsaW50LCBkYXRhLmZyYW1lKHRhYmxlKGhlcl9hbGxpbnQkaWRfY3BfaW50ZXJhY3Rpb25baGVyX2FsbGludCRoZWFsdGh5X2V4cD4wXSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEsIGFsbC54ID0gVCkKICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgZGF0YS5mcmFtZSh0YWJsZShoZXJfYWxsaW50JGlkX2NwX2ludGVyYWN0aW9uW2hlcl9hbGxpbnQkZW1ib2xpc2VkX2V4cD4wXSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEsIGFsbC54ID0gVCkKICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgCiAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUodGFibGUoaGVyX2FsbGludCRpZF9jcF9pbnRlcmFjdGlvbltoZXJfYWxsaW50JHJlZ2VuZXJhdGluZ19leHA+MF0pKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gMSwgYWxsLnggPSBUKQogIGNvbG5hbWVzKGhlcl9hbGxpbnQpWzk6MTFdID0gYygiaGVhbHRoeV9uIiwgImVtYm9saXNlZF9uIiwgInJlZ2VuZXJhdGluZ19uIikKICBoZXJfYWxsaW50W2lzLm5hKGhlcl9hbGxpbnQpXSA9IDAKICBoZXJfYWxsaW50JGN0X3BhaXIgPSBmYWN0b3IocGFzdGUwKGhlcl9hbGxpbnQkY3QxLCAiXyIsIGhlcl9hbGxpbnQkY3QyKSkKICAKICBjb21iX2NvbmQgPSBjb21ibihjb2xuYW1lcyhoZXJfYWxsaW50KVs2OjhdLDIpCiAgY29sbmFtZXMoY29tYl9jb25kKSA9IGMoImhlIiwgImhyIiwgImVyIikKICBmb3IoaSBpbiBjb2xuYW1lcyhjb21iX2NvbmQpKXsKICAgIHBsb3RfZGYgPSBoZXJfYWxsaW50W2hlcl9hbGxpbnRbLGNvbWJfY29uZFsxLGldXT4wIHwgaGVyX2FsbGludFssY29tYl9jb25kWzIsaV1dPjAsMToxMl0KICAgIAogICAgZXhwX2RmMSA9IHJlc2hhcGUyOjpkY2FzdChwbG90X2RmLCBmb3JtdWxhID0gaWRfY3BfaW50ZXJhY3Rpb24gfiBjdF9wYWlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUudmFyID0gY29tYl9jb25kWzEsaV0sIGZpbGwgPSAwKQogICAgcm93bmFtZXMoZXhwX2RmMSkgPSBleHBfZGYxWywxXQogICAgZXhwX2RmMSA9IGV4cF9kZjFbLC0xXT4wCiAgICBleHBfZGYyID0gcmVzaGFwZTI6OmRjYXN0KHBsb3RfZGYsIGZvcm11bGEgPSBpZF9jcF9pbnRlcmFjdGlvbiB+IGN0X3BhaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS52YXIgPSBjb21iX2NvbmRbMixpXSwgZmlsbCA9IDApCiAgICByb3duYW1lcyhleHBfZGYyKSA9IGV4cF9kZjJbLDFdCiAgICBleHBfZGYyID0gZXhwX2RmMlssLTFdPjAKICAgIAogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGluZm90aGVvOjptdXRpbmZvcm1hdGlvbihleHBfZGYxW3gsXSwgZXhwX2RmMlt4LF0pKSwKICAgICAgICAgICAgICAgICAgICBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKICAgIHBsb3RfZGYgPSBtZXJnZShwbG90X2RmLCBzYXBwbHkocm93bmFtZXMoZXhwX2RmMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBlMTA3MTo6aGFtbWluZy5kaXN0YW5jZShleHBfZGYxW3gsXSwgZXhwX2RmMlt4LF0pKSwKICAgICAgICAgICAgICAgICAgICBieS54ID0gMSwgYnkueSA9IDAsIGFsbC54ID0gVCkKICAgIHBsb3RfZGYgPSBtZXJnZShwbG90X2RmLCBzYXBwbHkocm93bmFtZXMoZXhwX2RmMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBlMTA3MTo6aGFtbWluZy5kaXN0YW5jZShleHBfZGYxW3gsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwX2RmMlt4LF0pL3N1bShleHBfZGYxW3gsXSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBfZGYyW3gsXSkpLAogICAgICAgICAgICAgICAgICAgIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQogICAgcGxvdF9kZiA9IG1lcmdlKHBsb3RfZGYsIHNhcHBseShyb3duYW1lcyhleHBfZGYxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHN1bShleHBfZGYxW3gsXSB8IGV4cF9kZjJbeCxdKSksCiAgICAgICAgICAgICAgICAgICAgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCiAgICAKICAgIGNvbG5hbWVzKHBsb3RfZGYpWzEzOjE2XSA9IHBhc3RlMChjKCJtdXRJbmZvXyIsICJoYW1tXyIsICJoYW1tTm9ybV8iLCAidG90XyIpLCBpKQogICAgCiAgICBoZXJfYWxsaW50ID0gbWVyZ2UoaGVyX2FsbGludCwgdW5pcXVlKHBsb3RfZGZbLGMoMSwxMzoxNildKSwgYWxsLnggPSBULCBieSA9IDEpCiAgfQogIGhlcl9hbGxpbnQkbXV0SW5mb19lcltpcy5uYShoZXJfYWxsaW50JG11dEluZm9fZXIpXSA9IDEKICBoZXJfYWxsaW50JG11dEluZm9faHJbaXMubmEoaGVyX2FsbGludCRtdXRJbmZvX2hyKV0gPSAxCiAgaGVyX2FsbGludCRtdXRJbmZvX2hlW2lzLm5hKGhlcl9hbGxpbnQkbXV0SW5mb19oZSldID0gMQogIGhlcl9hbGxpbnQkdG90X2hlW2lzLm5hKGhlcl9hbGxpbnQkdG90X2hlKV0gPSAwCiAgaGVyX2FsbGludCR0b3RfaHJbaXMubmEoaGVyX2FsbGludCR0b3RfaHIpXSA9IDAKICBoZXJfYWxsaW50JHRvdF9lcltpcy5uYShoZXJfYWxsaW50JHRvdF9lcildID0gMAogIGhlcl9hbGxpbnQkaGFtbV9lcltpcy5uYShoZXJfYWxsaW50JGhhbW1fZXIpXSA9IDAKICBoZXJfYWxsaW50JGhhbW1faHJbaXMubmEoaGVyX2FsbGludCRoYW1tX2hyKV0gPSAwCiAgaGVyX2FsbGludCRoYW1tX2hlW2lzLm5hKGhlcl9hbGxpbnQkaGFtbV9oZSldID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1fZXJbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9lcildID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1faGVbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9oZSldID0gMAogIGhlcl9hbGxpbnQkaGFtbU5vcm1faHJbaXMubmEoaGVyX2FsbGludCRoYW1tTm9ybV9ocildID0gMAogIAogIGhlcl9hbGxpbnQkZGlmZl9uX2hlID0gaGVyX2FsbGludCRlbWJvbGlzZWRfbi1oZXJfYWxsaW50JGhlYWx0aHlfbgogIGhlcl9hbGxpbnQkZGlmZl9uX2hyID0gaGVyX2FsbGludCRyZWdlbmVyYXRpbmdfbi1oZXJfYWxsaW50JGhlYWx0aHlfbgogIGhlcl9hbGxpbnQkZGlmZl9uX2VyID0gaGVyX2FsbGludCRyZWdlbmVyYXRpbmdfbi1oZXJfYWxsaW50JGVtYm9saXNlZF9uCiAgCiAgIyBwbG90IHRvdCB2cyBtdXR1YWwKICBwbG90X2RmID0gdW5pcXVlKGhlcl9hbGxpbnRbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgcGFzdGUwKGMoIm11dEluZm9fIiwgInRvdF8iLCAiZGlmZl9uXyIpLCBpKSldKQogIHBsdCA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IHRvdF9lciwgeSA9IG11dEluZm9fZXIqKGRpZmZfbl9lci9hYnMoZGlmZl9uX2VyKSkpKSsKICAgIGdlb21fYmluMmQoKSsKICAgIHNjYWxlX3hfbG9nMTAoKSsKICAgIHRoZW1lX2J3KCkrCiAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQogIHByaW50KHBsdCkKICAKICBubiA9IGlmKHNpbXApICJzaW1wIiBlbHNlICJhbGwiCiAgaGVyX2FsbGludF9sW1tubl1dID0gaGVyX2FsbGludAp9CgpzYXZlUkRTKGhlcl9hbGxpbnRfbCwgZmlsZSA9ICIuL3Jlc3VsdHMvY2VsbF9jb21tL3VwZHQvaW50ZXJhY3Rpb25zX211dEluZm9fY29uZENvbXAuUkRTIikKYGBgCgpHU0VBIG9mIGludGVyYWN0aW9ucyB1c2luZyBtdXR1YWwgaW5mb3JtYXRpb24KCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhoZXJfYWxsaW50X2wpKXsKICBoZXJfYWxsaW50ID0gaGVyX2FsbGludF9sW1tuXV0KICAKICBjb21iX2NvbmQgPSBjb21ibihjb2xuYW1lcyhoZXJfYWxsaW50KVs2OjhdLDIpCiAgY29sbmFtZXMoY29tYl9jb25kKSA9IGMoImhlIiwgImhyIiwgImVyIikKICBnc2VhX2xpc3QgPSBsaXN0KCkKICBmb3IoaSBpbiBjb2xuYW1lcyhjb21iX2NvbmQpKXsKICAgIHZhbHMgPSB1bmlxdWUoaGVyX2FsbGludFssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCBwYXN0ZTAoIm11dEluZm9fIiwgaSkpXSkKICAgIHZhbHMyID0gdmFsc1sscGFzdGUwKCJtdXRJbmZvXyIsIGkpXSAKICAgIG5hbWVzKHZhbHMyKSA9IHZhbHMkaWRfY3BfaW50ZXJhY3Rpb24KICAgIAogICAgc2V0LnNlZWQoMSkKICAgIGdzZWFfbGlzdFtbaV1dID0gbGlnZXI6OmJ1bGsuZ3NlYSgxLXZhbHMyLCBzZXQubGlzdCA9IGdyX2xpc3QsIG4ucmFuZCA9IDEwMDAwMDApCiAgICBnc2VhX2xpc3RbW2ldXSRncm91cCA9IHJvd25hbWVzKGdzZWFfbGlzdFtbaV1dKQogICAgZ3NlYV9saXN0W1tpXV0kY29tcCA9IGkKICB9CiAgCiAgbGlnZXI6OmdzZWEoMS12YWxzMiwgZ2VuZXNldCA9IGdyX2xpc3QkaW1tdW5lLCBwbG90ID0gVCkKICAKICAjIHBsb3QgR1NFQSBlbnJpY2htZW50CiAgcGxvdF9kZiA9IFJlZHVjZShyYmluZCwgZ3NlYV9saXN0KQogIHBsb3RfZGYkcS52YWwgPSAtbG9nMTAocGxvdF9kZiRxLnZhbCswLjAwMDAwMDUpKihwbG90X2RmJHNzY29yZS9hYnMocGxvdF9kZiRzc2NvcmUpKQogIHBsb3RfZGYkY29tcCA9IGZhY3RvcihwbG90X2RmJGNvbXAsIGxldmVscyA9IHJldihjKCJoZSIsImhyIiwiZXIiKSkpCiAgbSA9IHRhcHBseShwbG90X2RmJHEudmFsLCBwbG90X2RmJGdyb3VwLG1lYW4pCiAgcGxvdF9kZiRncm91cCA9IGZhY3RvcihwbG90X2RmJGdyb3VwLCBsZXZlbHMgPSBuYW1lcyhtKVtvcmRlcihtLCBkZWNyZWFzaW5nID0gRildKQogIHBsdCA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IHEudmFsLCB5ID0gZ3JvdXAsIGZpbGwgPSBjb21wKSkrCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhsb2cxMCgwLjA1KSwgLWxvZzEwKDAuMDUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIikrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDApKSsKICAgIGxhYnMoeCA9ICJxLXZhbHVlIHggc2NvcmUgc2lnbmFsIiwgeSA9ICJpbnRlcmFjdGlvbiB0eXBlIiwgZmlsbCA9ICJjb21wYXJpc29uIikrCiAgICB0aGVtZV9idygpKwogICAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIHNpemUgPSA3KSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDcuNSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAsMSksCiAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoLTAuMDUsMS4wNSksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcuNSksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKDAsMCwwLDApLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjM1LCAiY20iKSkKICBwcmludChwbHQpCiAgCiAgc2F2ZVJEUyhnc2VhX2xpc3QsIGZpbGUgPSBwYXN0ZTAoIi4vcmVzdWx0cy9jZWxsX2NvbW0vdXBkdC8iLCBuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnc2VhX2ludGVyYWN0aW9uc19tdXRJbmZvX2NvbmRDb21wLlJEUyIpKQp9CmBgYAoKU2F2ZSBtdXR1YWwgaW5mb3JtYXRpb24gZm9yIExSIGFuZCBjZWxsIHR5cGVzCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoaGVyX2FsbGludF9sKSl7CiAgaGVyX2FsbGludCA9IGhlcl9hbGxpbnRfbFtbbl1dCiAgIyBzZWxlY3QgZ2VuZXMgZnJvbSBsb3dlc3QgbXV0dWFsICh0YWJsZSAtIGdldCBtb3N0IGNvbW1vbikKICAjICsKICAjIG1lYW4gbXV0dWFsIHBlciBjZWxsIHR5cGUgKGxvd2VzdCA9IG1vcmUgY2hhbmdlKQogICMjIHBsb3QgaW50ZXJhY3Rpb25zIGJhc2VkIG9uIHRob3NlIGdlbmVzIChzb21lIGFyZSBpbnZvbHZlZCBpbiBtb3JlIHRoYW4gb25lKQogICMjIGhlYXRtYXAgLSByb3dzIGN0OyBjb2x1bW5zIGdlbmVzOyBnYXBzIGJldHdlZW4gaW50ZXJhY3Q7IDEgaGVhdG1hcC9jb25kLCBzYW1lIGN0IG9yZGVyaW5nCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hlPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDEiLCJtdXRJbmZvX2hlIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwiY3QyIiwibXV0SW5mb19oZSIpXSkKICBjdF9tdXRfaGUgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9faGUsIHN1Yl9kZjIkbXV0SW5mb19oZSksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkY3QxLCBzdWJfZGYyJGN0MiksIG1lYW4pCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDEiLCJtdXRJbmZvX2hyIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9ocj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwiY3QyIiwibXV0SW5mb19ociIpXSkKICBjdF9tdXRfaHIgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9faHIsIHN1Yl9kZjIkbXV0SW5mb19ociksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkY3QxLCBzdWJfZGYyJGN0MiksIG1lYW4pCiAgc3ViX2RmMSA9IHVuaXF1ZShoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAsYygiaWRfY3BfaW50ZXJhY3Rpb24iLCJjdDEiLCJtdXRJbmZvX2VyIildKQogIHN1Yl9kZjIgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwiY3QyIiwibXV0SW5mb19lciIpXSkKICBjdF9tdXRfZXIgPSB0YXBwbHkoYyhzdWJfZGYxJG11dEluZm9fZXIsIHN1Yl9kZjIkbXV0SW5mb19lciksIAogICAgICAgICAgICAgICAgICAgICBjKHN1Yl9kZjEkY3QxLCBzdWJfZGYyJGN0MiksIG1lYW4pCiAgY3RfbXV0X2RmID0gY2JpbmQoY3RfbXV0X2hlLGN0X211dF9ocixjdF9tdXRfZXIpCiAgcm93bmFtZXMoY3RfbXV0X2RmKSA9IG5hbWVzKGN0X211dF9oZSkKICBjb2xuYW1lcyhjdF9tdXRfZGYpID0gYygiaGUiLCAiaHIiLCAiZXIiKQogIHNhdmVSRFMoY3RfbXV0X2RmLCBmaWxlID0gIi4vcmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9jdF9zZWxlY3RfbXV0SW5mb19jb25kQ29tcC5SRFMiKQogIAogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19oZSIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9faGUiKV0pCiAgbHJfbXV0X2hlID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2hlLCBzdWJfZGYyJG11dEluZm9faGUpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9ocj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19ociIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9faHIiKV0pCiAgbHJfbXV0X2hyID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2hyLCBzdWJfZGYyJG11dEluZm9faHIpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIHN1Yl9kZjEgPSB1bmlxdWUoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wLGMoImlkX2NwX2ludGVyYWN0aW9uIiwibHIxIiwibXV0SW5mb19lciIpXSkKICBzdWJfZGYyID0gdW5pcXVlKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfZXI+MCxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsImxyMiIsIm11dEluZm9fZXIiKV0pCiAgbHJfbXV0X2VyID0gdGFwcGx5KGMoc3ViX2RmMSRtdXRJbmZvX2VyLCBzdWJfZGYyJG11dEluZm9fZXIpLCAKICAgICAgICAgICAgICAgICAgICAgYyhzdWJfZGYxJGxyMSwgc3ViX2RmMiRscjIpLCBtZWFuKQogIAogIGxyX2NudF9oZSA9IHRhYmxlKGMoaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9oZT4wICYgaGVyX2FsbGludCRtdXRJbmZvX2hlPD0wLjA1LCJscjEiXSwKICAgICAgICAgICAgICAgICAgICAgIGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaGU+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19oZTw9MC4wNSwibHIyIl0pKQogIGxyX2hlID0gbWVyZ2UobHJfY250X2hlLCBscl9tdXRfaGUsIGJ5LnggPSAxLCBieS55ID0gMCkKICBscl9jbnRfaHIgPSB0YWJsZShjKGhlcl9hbGxpbnRbaGVyX2FsbGludCR0b3RfaHI+MCAmIGhlcl9hbGxpbnQkbXV0SW5mb19ocjw9MC4wNSwibHIxIl0sCiAgICAgICAgICAgICAgICAgICAgICBoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2hyPjAgJiBoZXJfYWxsaW50JG11dEluZm9faHI8PTAuMDUsImxyMiJdKSkKICBscl9ociA9IG1lcmdlKGxyX2NudF9ociwgbHJfbXV0X2hyLCBieS54ID0gMSwgYnkueSA9IDApCiAgbHJfY250X2VyID0gdGFibGUoYyhoZXJfYWxsaW50W2hlcl9hbGxpbnQkdG90X2VyPjAgJiBoZXJfYWxsaW50JG11dEluZm9fZXI8PTAuMDUsImxyMSJdLAogICAgICAgICAgICAgICAgICAgICAgaGVyX2FsbGludFtoZXJfYWxsaW50JHRvdF9lcj4wICYgaGVyX2FsbGludCRtdXRJbmZvX2VyPD0wLjA1LCJscjIiXSkpCiAgbHJfZXIgPSBtZXJnZShscl9jbnRfZXIsIGxyX211dF9lciwgYnkueCA9IDEsIGJ5LnkgPSAwKQogIAogICMgRUNNIC0gd2hpY2ggcHJvdGVpbnMvY29sbGFnZW5zPzsgbWVudGlvbiBUR0ZCCiAgIyBkZXYgLSB3aGljaCBsaWdhbmRzL3JlY2VwdG9ycwogIGxyX2FsbCA9IHJiaW5kKGxyX2hlLCBscl9ociwgbHJfZXIpCiAgbHJfYWxsJGNvbmQgPSBjKHJlcCgiaGUiLCBucm93KGxyX2hlKSksIHJlcCgiaHIiLCBucm93KGxyX2hyKSkscmVwKCJlciIsIG5yb3cobHJfZXIpKSkKICBjb2xuYW1lcyhscl9hbGwpID0gYygibHIiLCAibl9tdXQuMDUiLCAibWVhbl9tdXRJbmZvIiwgImNvbmQiKQogIHNhdmVSRFMobHJfYWxsLCBmaWxlID0gcGFzdGUwKCIuL3Jlc3VsdHMvY2VsbF9jb21tL3VwZHQvIiwgbiwgIkxSX3NlbGVjdF9tdXRJbmZvX2NvbmRDb21wLlJEUyIpKQp9CmBgYAoKUGxvdCBpbnRlcmFjdGlvbnMKCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhjdF9pbnRfZXhwX2wpKXsKICBjdF9pbnRfZXhwID0gY3RfaW50X2V4cF9sW1tuXV0KICByID0gaWYobj09ImFsbCIpIDE6MyBlbHNlIDQ6NgogIAogICMgaW50ZXJhY3Rpb25zCiAgaW50ZGYgPSB1bmlxdWUoUmVkdWNlKHJiaW5kLCByZWZvcm1fbGlzdFtyXSlbLGMoMSw0LDUpXSkKICBpbnRkZiRpbnRwYWlyID0gcGFzdGUwKGludGRmJGxyMSwgIiAtICIsIGludGRmJGxyMikKICAKICBjdF9pbnRfZXhwX2ZpbGUgPSB1bmlxdWUoY3RfaW50X2V4cFssYygxLDQ6NSw3OjEwKV0pCiAgY3RfaW50X2V4cF9maWxlJGN0cGFpciA9IHBhc3RlMChjdF9pbnRfZXhwX2ZpbGUkY3QsICIgLSAiLCBjdF9pbnRfZXhwX2ZpbGUkY3RfdGFyZ2V0KQogIAogIHBsb3RfZGZfaW50ID0gcmVzaGFwZTI6Om1lbHQoY3RfaW50X2V4cF9maWxlWyxjKDEsOCw0OjcpXSkKICBwbG90X2RmX2ludCR2YXJpYWJsZSA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHBsb3RfZGZfaW50JHZhcmlhYmxlKSwgIl8iKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHhbWzFdXVsxXSkpCiAgcGxvdF9kZl9pbnQkdmFyaWFibGUgPSBmYWN0b3IocGxvdF9kZl9pbnQkdmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHJldihjKCJoZWFsdGh5IiwgImVtYm9saXNlZCIsICJyZWdlbmVyYXRpbmciKSkpCiAgCiAgc3ViX3Bsb3RfZGZfaW50ID0gcGxvdF9kZl9pbnRbZ3JlcGwoIlN0ZWxsYXRlIiwgcGxvdF9kZl9pbnQkY3RwYWlyKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiS3VwZmZlciIsIHBsb3RfZGZfaW50JGN0cGFpcikgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkxTRUMiLCBwbG90X2RmX2ludCRjdHBhaXIpLF0KICAKICBzdWJfcGxvdF9kZl9pbnQgPSBtZXJnZShzdWJfcGxvdF9kZl9pbnQsIGludGRmWyxjKDEsNCldLCBieSA9IDEsIGFsbC54ID0gVCkKICAKICBnZyA9ICJFQ00iCiAgcGx0ID0gZ2dwbG90KHN1Yl9wbG90X2RmX2ludFtzdWJfcGxvdF9kZl9pbnQkZGVzY3JpcHRpb249PWdnLF0sIAogICAgICAgICBhZXMoeCA9IGN0cGFpciwgeSA9IHZhcmlhYmxlLCBjb2xvdXIgPSB2YWx1ZSwgc2l6ZSA9IHZhbHVlKSkrCiAgICBmYWNldF9ncmlkKGludHBhaXJ+LikrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJleHAiLCByZXZlcnNlID0gVCksIAogICAgICAgICAgIGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJleHAiLCByZXZlcnNlID0gVCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgbGFicyh0aXRsZSA9IGdnKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSwKICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIHNpemUgPSA4KSkKICBwcmludChwbHQpCn0KYGBgCgpJbXBvcnRhbnQgdGFibGVzCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoaW50ZXJfZGYpKXsKICB3cml0ZS5jc3YoaW50ZXJfZGZbW25dXSwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGLAogICAgICAgICAgICBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS91cGR0L3RhYmxlcy9JbnRlcmFjdF8iLCBuLCAiX2NlbGx0eXBlc19jb25kLmNzdiIpKQp9CmZvcihuIGluIG5hbWVzKGN0X2ludF9leHBfbCkpewogIGN0X2ludF9leHAgPSBjdF9pbnRfZXhwX2xbW25dXQogIHdyaXRlLmNzdihjdF9pbnRfZXhwLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYsIAogICAgICAgICAgICBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS91cGR0L3RhYmxlcy8iLCBuLCAiaW50ZXJhY3RfY2VsbHR5cGVfZXhwX2dyb3VwLmNzdiIpKQp9CmBgYAoKCiMjIENlbGwtY2VsbCBjb21tdW5pY2F0aW9uIG5ldHdvcmtzCkxvYWQgZGF0YSB0byBtYWtlIGNlbGwgY29tbSBuZXR3b3JrcyAoTk9UIFVTRUQgSEVSRSkKCmBgYHtyfQpyZWRvbmVfbWV0YSA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKGFsbGNlbGxzX2NzcyRDb25kaXRpb24pKXsKICByZWRvbmVfbWV0YVtbY2NdXSA9IHJlYWQudGFibGUocGFzdGUwKCJyZXN1bHRzL2NlbGxfY29tbS9DZWxsUGhvbmVEQl9ydW5zX3VwZHQvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYywgIi8iLCBjYywgIl9tZXRhX25hbWVzLnR4dCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSkKfQpyZWRvbmVfbWV0YV9hbGwgPSBSZWR1Y2UocmJpbmQsIHJlZG9uZV9tZXRhKQoKYWxsY2VsbHNfcmVkb25lID0gQWRkTWV0YURhdGEoYWxsY2VsbHNfY3NzLCByZWRvbmVfbWV0YV9hbGwpCmFsbGNlbGxzX3JlZG9uZSA9IGFsbGNlbGxzX3JlZG9uZVssIWlzLm5hKGFsbGNlbGxzX3JlZG9uZSRjZWxsX3R5cGUpXQpgYGAKCkZ1bmN0aW9ucyB1c2VkIHRvIG1ha2UgY2VsbCBjb21tIG5ldHdvcmtzCgpgYGB7cn0KbWFrZU1lZGlhbiA9IGZ1bmN0aW9uKHBvaW50X2RmLCBlZGdlX2RmLCBjbCA9IGMoImN0MiIsICJtYWpfZzEiLCAibWFqX2cyIikpewogIG1lYW5fbWFqb3IgPSBkYXRhLmZyYW1lKCJYMSIgPSB0YXBwbHkocG9pbnRfZGYkWDEsIHBvaW50X2RmWyxjbFsxXV0sIG1lZGlhbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlgyIiA9IHRhcHBseShwb2ludF9kZiRYMiwgcG9pbnRfZGZbLGNsWzFdXSwgbWVkaWFuKSkKICBtZWFuX21ham9yWyxjbFsxXV0gPSByb3duYW1lcyhtZWFuX21ham9yKQogIAogIGVkZ2VfZGZbLGNsWzJdXSA9IGZhY3RvcihlZGdlX2RmWyxjbFsyXV0sIGxldmVscyA9IHVuaXF1ZShjKGVkZ2VfZGZbLGNsWzJdXSwgZWRnZV9kZlssY2xbM11dKSkpCiAgZWRnZV9kZlssY2xbM11dID0gZmFjdG9yKGVkZ2VfZGZbLGNsWzNdXSwgbGV2ZWxzID0gdW5pcXVlKGMoYXMuY2hhcmFjdGVyKGVkZ2VfZGZbLGNsWzJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWRnZV9kZlssY2xbM11dKSkpCiAgCiAgbWFqX21hdCA9IHRhYmxlKGVkZ2VfZGZbLGNsWzJdXSwgZWRnZV9kZlssY2xbM11dKQogIGRpYWcobWFqX21hdCkgPSAwCiAgZWRnZV9tYWpvciA9IGRhdGEuZnJhbWUobWFqX21hdCArIHQobWFqX21hdCkpCiAgZWRnZV9tYWpvciA9IG1lcmdlKGVkZ2VfbWFqb3IsIG1lYW5fbWFqb3IsIGJ5LnggPSAxLCBieS55ID0gMykKICBlZGdlX21ham9yID0gbWVyZ2UoZWRnZV9tYWpvciwgbWVhbl9tYWpvciwgYnkueCA9IDIsIGJ5LnkgPSAzKQogIAogIGNsY29tYiA9IGNvbWJuKHVuaXF1ZShjKGFzLmNoYXJhY3RlcihlZGdlX21ham9yJFZhcjEpLCBhcy5jaGFyYWN0ZXIoZWRnZV9tYWpvciRWYXIyKSkpLCAyKQogIGtlZXAgPSBjKCkKICBmb3IoaiBpbiAxOm5jb2woY2xjb21iKSl7CiAgICBrZWVwID0gYyhrZWVwLCB3aGljaChlZGdlX21ham9yJFZhcjI9PWNsY29tYlsxLGpdICYgZWRnZV9tYWpvciRWYXIxPT1jbGNvbWJbMixqXSkpCiAgfQogIGVkZ2VfbWFqb3IgPSBlZGdlX21ham9yW2tlZXAsXQogIAogIHJldHVybihsaXN0KG1lYW5fbWFqb3IgPSBtZWFuX21ham9yLCBlZGdlX21ham9yID0gZWRnZV9tYWpvcikpCn0KCm1ha2VNZWRpYW5Db25kID0gZnVuY3Rpb24ocG9pbnRfZGYsIGVkZ2VfZGYsIGNsID0gYygiY3QiLCAiY3RfZzEiLCAiY3RfZzIiKSwgZWRnZV9ieSA9ICJjb25kaXRpb24iKXsKICBtZWFuX21ham9yID0gZGF0YS5mcmFtZSgiWDEiID0gdGFwcGx5KHBvaW50X2RmJFgxLCBwb2ludF9kZlssY2xbMV1dLCBtZWRpYW4pLAogICAgICAgICAgICAgICAgICAgICAgICAgICJYMiIgPSB0YXBwbHkocG9pbnRfZGYkWDIsIHBvaW50X2RmWyxjbFsxXV0sIG1lZGlhbikpCiAgbWVhbl9tYWpvclssY2xbMV1dID0gcm93bmFtZXMobWVhbl9tYWpvcikKICAKICBlZGdlX2RmWyxjbFsyXV0gPSBmYWN0b3IoZWRnZV9kZlssY2xbMl1dLCBsZXZlbHMgPSB1bmlxdWUoYyhlZGdlX2RmWyxjbFsyXV0sIGVkZ2VfZGZbLGNsWzNdXSkpKQogIGVkZ2VfZGZbLGNsWzNdXSA9IGZhY3RvcihlZGdlX2RmWyxjbFszXV0sIGxldmVscyA9IHVuaXF1ZShjKGFzLmNoYXJhY3RlcihlZGdlX2RmWyxjbFsyXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfZGZbLGNsWzNdXSkpKQogIAogIGVkZ2VfbCA9IGxpc3QoKQogIGZvcihpIGluIHVuaXF1ZShlZGdlX2RmWyxlZGdlX2J5XSkpewogICAgc3ViX2VkZ2VfZGYgPSBlZGdlX2RmW2VkZ2VfZGZbLGVkZ2VfYnldPT1pLF0KICAgIG1hal9tYXQgPSB0YWJsZShzdWJfZWRnZV9kZlssY2xbMl1dLCBzdWJfZWRnZV9kZlssY2xbM11dKQogICAgZGlhZyhtYWpfbWF0KSA9IDAKICAgIGVkZ2VfbWFqb3IgPSBkYXRhLmZyYW1lKG1hal9tYXQgKyB0KG1hal9tYXQpKQogICAgZWRnZV9tYWpvciA9IG1lcmdlKGVkZ2VfbWFqb3IsIG1lYW5fbWFqb3IsIGJ5LnggPSAxLCBieS55ID0gMykKICAgIGVkZ2VfbWFqb3IgPSBtZXJnZShlZGdlX21ham9yLCBtZWFuX21ham9yLCBieS54ID0gMiwgYnkueSA9IDMpCiAgICAKICAgICMgcmVtb3ZlIHJlcGVhdGVkCiAgICBjbGNvbWIgPSBjb21ibih1bmlxdWUoYyhhcy5jaGFyYWN0ZXIoZWRnZV9tYWpvciRWYXIxKSwgYXMuY2hhcmFjdGVyKGVkZ2VfbWFqb3IkVmFyMikpKSwgMikKICAgIGtlZXAgPSBjKCkKICAgIGZvcihqIGluIDE6bmNvbChjbGNvbWIpKXsKICAgICAga2VlcCA9IGMoa2VlcCwgd2hpY2goZWRnZV9tYWpvciRWYXIyPT1jbGNvbWJbMSxqXSAmIGVkZ2VfbWFqb3IkVmFyMT09Y2xjb21iWzIsal0pKQogICAgfQogICAgCiAgICBlZGdlX2xbW2ldXSA9IGVkZ2VfbWFqb3Jba2VlcCxdCiAgfQogIGVkZ2VfbWFqb3IgPSBSZWR1Y2UocmJpbmQsIGVkZ2VfbCkKICBlZGdlX21ham9yWyxlZGdlX2J5XSA9IHVubGlzdChsYXBwbHkobmFtZXMoZWRnZV9sKSwgZnVuY3Rpb24oeCkgcmVwKHgsIG5yb3coZWRnZV9sW1t4XV0pKSkpCiAgZWRnZV9tYWpvciA9IGVkZ2VfbWFqb3JbZWRnZV9tYWpvciRWYXIyIT1lZGdlX21ham9yJFZhcjEsXQogIAogIHJldHVybihsaXN0KG1lYW5fbWFqb3IgPSBtZWFuX21ham9yLCBlZGdlX21ham9yID0gZWRnZV9tYWpvcikpCn0KYGBgCgpQbG90IGxpZ2FuZHMgYW5kIHJlY2VwdG9ycyB3aXRoIE1EUwoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD00LjZ9CmludGVyX2RmID0gcmVhZFJEUyhmaWxlID0gInJlc3VsdHMvY2VsbF9jb21tL3VwZHQvY29uZF9kaWZmX2ludGVyYWN0X0RFLlJEUyIpCgojIGludGVyYWN0aW9ucyB1bmlxdWUgdG8gZWFjaCBjb25kaXRpb24KdW5pcXVlX2ludGVycyA9IGMoc2V0ZGlmZihpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjKGludGVyX2RmJGVtYm9saXNlZCRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGlkX2NwX2ludGVyYWN0aW9uKSksCiAgICAgICAgICAgICAgICAgIHNldGRpZmYoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjKGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgICBzZXRkaWZmKGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyhpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24pKSkKCiMgaW50ZXJhY3Rpb25zIHVuaXF1ZSB0byBoZWFsdGh5IG9yIHRvIGVtYi9yZWdlbgpjb21waF9pbnRlcnMgPSBjKHNldGRpZmYoaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyhpbnRlcl9kZiRlbWJvbGlzZWQkaWRfY3BfaW50ZXJhY3Rpb24sIGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbikpLAogICAgICAgICAgICAgICAgIHNldGRpZmYoaW50ZXJfZGYkZW1ib2xpc2VkJGlkX2NwX2ludGVyYWN0aW9uLCBpbnRlcl9kZiRoZWFsdGh5JGlkX2NwX2ludGVyYWN0aW9uKSwKICAgICAgICAgICAgICAgICBzZXRkaWZmKGludGVyX2RmJHJlZ2VuZXJhdGluZyRpZF9jcF9pbnRlcmFjdGlvbiwgaW50ZXJfZGYkaGVhbHRoeSRpZF9jcF9pbnRlcmFjdGlvbikpCgojIHByZXBhcmUgZ2VuZSBwYWlycyBwZXIgY29uZGl0aW9uCmdlbmVfcGFpcnNfY29uZCA9IHJiaW5kKHVuaXF1ZShpbnRlcl9kZiRoZWFsdGh5WyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsICJnbjEiLCAiZ24yIildKSwKICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGludGVyX2RmJGVtYm9saXNlZFssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAiZ24xIiwgImduMiIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShpbnRlcl9kZiRyZWdlbmVyYXRpbmdbLGMoImlkX2NwX2ludGVyYWN0aW9uIiwgImduMSIsICJnbjIiKV0pKQpnZW5lX3BhaXJzX2NvbmQkY29uZGl0aW9uID0gYyhyZXAoImhlYWx0aHkiLCBucm93KHVuaXF1ZShpbnRlcl9kZiRoZWFsdGh5WyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbjEiLCAiZ24yIildKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoImVtYm9saXNlZCIsIG5yb3codW5pcXVlKGludGVyX2RmJGVtYm9saXNlZFssYygiaWRfY3BfaW50ZXJhY3Rpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImduMSIsICJnbjIiKV0pKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgicmVnZW5lcmF0aW5nIiwgbnJvdyh1bmlxdWUoaW50ZXJfZGYkcmVnZW5lcmF0aW5nWyxjKCJpZF9jcF9pbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbjEiLCAiZ24yIildKSkpKQoKIyBsaXN0IGFsbCBMUiBnZW5lcwphbGxfbHJfZ2VuZXMgPSB1bmlxdWUoYyhhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkaGVhbHRoeSRnbjEpLCBhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkaGVhbHRoeSRnbjIpLAogICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkZW1ib2xpc2VkJGduMSksIGFzLmNoYXJhY3RlcihpbnRlcl9kZiRlbWJvbGlzZWQkZ24yKSwKICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGludGVyX2RmJHJlZ2VuZXJhdGluZyRnbjEpLCBhcy5jaGFyYWN0ZXIoaW50ZXJfZGYkcmVnZW5lcmF0aW5nJGduMikpKQphbGxfbHJfZ2VuZXMgPSBhbGxfbHJfZ2VuZXNbYWxsX2xyX2dlbmVzICVpbiUgcm93bmFtZXMoc3ViX2FsbGNlbGxzX2Nzc0Bhc3NheXMkU0NUQGRhdGEpXQoKIyBjYWxjdWxhdGUgbWVhbiBwZXIgY2VsbCB0eXBlIGFuZCBjb25kaXRpb24gZm9yIGVhY2ggTFIgZ2VuZQptZWFuX2V4cF9jb25kX2xyID0gYXBwbHkoc3ViX2FsbGNlbGxzX2Nzc0Bhc3NheXMkU0NUQGRhdGFbYWxsX2xyX2dlbmVzLF0sIDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgdGFwcGx5KHgsIHBhc3RlMChzdWJfYWxsY2VsbHNfY3NzJHN1YnBvcHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsIHN1Yl9hbGxjZWxsc19jc3MkQ29uZGl0aW9uKSwgbWVhbikpCgojIGRldGVybWluZSB0aGUgY2VsbCB0eXBlIGFuZCBjb25kaXRpb24gd2l0aCB0aGUgaGlnaGVzdCBleHByZXNzaW9uCm1heF9jb25kX2N0ID0gcm93bmFtZXMobWVhbl9leHBfY29uZF9scilbYXBwbHkobWVhbl9leHBfY29uZF9sciwgMiwgd2hpY2gubWF4KV0KbmFtZXMobWF4X2NvbmRfY3QpID0gY29sbmFtZXMobWVhbl9leHBfY29uZF9scikKbWF4X2NvbmRfY3RfY3QgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KG1heF9jb25kX2N0LCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKbWF4X2NvbmRfY3RfY29uZCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQobWF4X2NvbmRfY3QsICJfIiksIGZ1bmN0aW9uKHgpIHhbMl0pKQoKIyBjb3JyZWxhdGlvbiBvZiBtZWFuIGV4cHJlc3Npb24KY29yX2NvbmRfbHIgPSBjb3IobWVhbl9leHBfY29uZF9sciwgbWV0aG9kID0gInNwIikKCiMgZmlsdGVyIGNvcnJlbGF0aW9uIHdpdGggaXRzZWxmLCBrZWVwIG9ubHkgZ2VuZXMgd2l0aCBjb3I+PTAuMwpkaWFnKGNvcl9jb25kX2xyKSA9IDAKYWRqX2NvbmRfbWF0ID0gY29yX2NvbmRfbHI+PTAuMwoKIyBidWlsZCBncmFwaCwgcHJvamVjdCB3aXRoIE1EUwpuZXR3b3JrX2NvbmQgPSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoYWRqX2NvbmRfbWF0LCB3ZWlnaHRlZD1ULCBtb2RlPSJ1bmRpcmVjdGVkIiwgZGlhZz1GKQpsX2NvbmQgPSBpZ3JhcGg6OmxheW91dF93aXRoX21kcyhuZXR3b3JrX2NvbmQpCmxfY29uZCA9IGRhdGEuZnJhbWUobF9jb25kKQpsX2NvbmQkZ2VuZSA9IGNvbG5hbWVzKGFkal9jb25kX21hdCkKcm93bmFtZXMobF9jb25kKSA9IGNvbG5hbWVzKGFkal9jb25kX21hdCkKCiMgZGVmaW5lIGFsbCBlZGdlcywgYmFzZWQgb24gQ2VsbFBob25lREIgcGFpcmluZ3MKdG1wX2RmID0gbWVyZ2UoZ2VuZV9wYWlyc19jb25kLCBsX2NvbmQsIGJ5LnggPSAiZ24xIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX2RmID0gbWVyZ2UodG1wX2RmLCBsX2NvbmQsIGJ5LnggPSAiZ24yIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX2RmID0gbWVyZ2UoZWRnZV9jb25kX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2N0KSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jdCksIGJ5LnggPSAyLCBieS55ID0gMCwgYWxsLnggPSBUKQpjb2xuYW1lcyhlZGdlX2NvbmRfZGYpWzk6MTBdID0gYygiY3RfZzEiLCAiY3RfZzIiKQojIGFkZCBoaWdoZXN0IGV4cHJlc3NpbmcgbWFqb3IgY2VsbCB0eXBlcwplZGdlX2NvbmRfZGYkbWFqX2cxID0gaWZlbHNlKGdyZXBsKCJMU0VDIiwgZWRnZV9jb25kX2RmJGN0X2cxKSwgIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiSGVwYXRvY3l0ZXMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfZGYkY3RfZzEpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgZWRnZV9jb25kX2RmJGN0X2cxKSwgIkNob2xhbmdpb2N5dGVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW1tdW5lIikpKSkKZWRnZV9jb25kX2RmJG1hal9nMiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIGVkZ2VfY29uZF9kZiRjdF9nMiksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIkhlcGF0b2N5dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlN0ZWxsYXRlIGNlbGxzIiwgZWRnZV9jb25kX2RmJGN0X2cyKSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIGVkZ2VfY29uZF9kZiRjdF9nMiksICJDaG9sYW5naW9jeXRlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkltbXVuZSIpKSkpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF9kZiA9IG1lcmdlKGVkZ2VfY29uZF9kZiwgZGF0YS5mcmFtZShtYXhfY29uZF9jdF9jb25kKSwgYnkueCA9IDIsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCgojIGRlZmluZSB0aGUgdmVydGljZXMgb2YgdGhlIG5ldHdvcmsKcG9pbnRfY29uZF9kZiA9IGxfY29uZApwb2ludF9jb25kX2RmJGN0ID0gbWF4X2NvbmRfY3RfY3RbcG9pbnRfY29uZF9kZiRnZW5lXQpwb2ludF9jb25kX2RmJGN0MiA9IGlmZWxzZShncmVwbCgiTFNFQyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJIZXBhdG9jeXRlcyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBwb2ludF9jb25kX2RmJGN0KSwgIk1lc2VuY2h5bWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJDaG9sYW5naW9jeXRlcyIsIHBvaW50X2NvbmRfZGYkY3QpLCAiQ2hvbGFuZ2lvY3l0ZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJbW11bmUiKSkpKQpwb2ludF9jb25kX2RmJGNvbmQgPSBtYXhfY29uZF9jdF9jb25kW3BvaW50X2NvbmRfZGYkZ2VuZV0KCiMgZGVmaW5lIHRoZSBtZWRpYW4gcG9pbnRzIGZvciBlYWNoIGNlbGwgdHlwZSAodXNpbmcgbWF4IGV4cHJlc3Npb24pCnBlX2wgPSBtYWtlTWVkaWFuKHBvaW50X2NvbmRfZGYsIGVkZ2VfY29uZF9kZiwgY2wgPSBjKCJjdCIsICJjdF9nMSIsICJjdF9nMiIpKQoKIyBwbG90IHRvdGFsIGdlbmUgY29ycmVsYXRpb24gcHJvamVjdGlvbiBhbmQgbWVkaWFuIG5ldHdvcmsKcGx0Ym90aCA9IGdncGxvdCgpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2NvbmRfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEYpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsNCwxOSkpKwogIGdlb21fc2VnbWVudChkYXRhID0gcGVfbFtbMl1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjE1LCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBlX2xbWzFdXSwgCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9sW1syXV0kRnJlcSkpKwogIHRoZW1lX2NsYXNzaWMoKSsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKcHJpbnQocGx0Ym90aCkKCnBsdGJvdGggPSBnZ3Bsb3QoKSsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IGVkZ2VfY29uZF9kZiwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnkpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjAzLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50X2NvbmRfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjYsIHNob3cubGVnZW5kID0gRikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCw0LDE5KSkrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfbFtbMV1dLCAKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2xbWzJdXSRGcmVxKSkrCiAgdGhlbWVfY2xhc3NpYygpKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpwcmludChwbHRib3RoKQoKIyBnZXQgbWVkaWFuIHBlciBjZWxsIHR5cGUsIHBlciBjb25kaXRpb24gLSBGVUxMIE5FVFdPUksKcGVfY29uZF9sID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF9kZiwgZWRnZV9jb25kX2RmLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUocGVfY29uZF9sW1syXV0kY29uZGl0aW9uKSl7CiAgcGx0X2NvbmRfbFtbY2NdXSA9IGdncGxvdCgpKwogICAgZ2VvbV9zZWdtZW50KGRhdGEgPSBwZV9jb25kX2xbWzJdXVtwZV9jb25kX2xbWzJdXSRjb25kaXRpb249PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IEZyZXEsIGFscGhhID0gRnJlcSkpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGVfY29uZF9sW1sxXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfY29uZF9sW1syXV0kRnJlcSkpKwogICAgc2NhbGVfYWxwaGFfY29udGludW91cyhsaW1pdHMgPSByYW5nZShwZV9jb25kX2xbWzJdXSRGcmVxKSkrCiAgICBsYWJzKHRpdGxlID0gY2MpKwogICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMiksCiAgICAgICAgICAgYWxwaGEgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMikpKwogICAgdGhlbWVfY2xhc3NpYygpCn0KcGx0X2NvbmRfbFtbImxlZyJdXSA9IGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X2NvbmRfbFtbImhlYWx0aHkiXV0pCgojIHBsb3QgRlVMTCBORVRXT1JLIG1lZGlhbiBwZXIgY29uZGl0aW9uCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbHRfY29uZF9sW1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xbWzNdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBwbHRfY29uZF9sJGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gVU5JUVVFIFBFUiBDT05EVElPTiBORVRXT1JLCnBlX2NvbmRfbF91ID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF9kZiwgZWRnZV9jb25kX2RmW2VkZ2VfY29uZF9kZiRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIHVuaXF1ZV9pbnRlcnMsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfY29uZF9sX3UgPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kaXRpb24pKXsKICBwbHRfY29uZF9sX3VbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfY29uZF9sX3VbWzJdXVtwZV9jb25kX2xfdVtbMl1dJGNvbmRpdGlvbj09Y2MsXSwgCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEsIGFscGhhID0gRnJlcSkpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcGVfY29uZF9sX3VbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfdVtbMl1dJEZyZXEpKSsKICAgIHNjYWxlX2FscGhhX2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UocGVfY29uZF9sX3VbWzJdXSRGcmVxKSkrCiAgICBsYWJzKHRpdGxlID0gY2MpKwogICAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMiksCiAgICAgICAgICAgYWxwaGEgPSBndWlkZV9sZWdlbmQoZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBucm93ID0gMikpKwogICAgdGhlbWVfY2xhc3NpYygpCn0KcGx0X2NvbmRfbF91W1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sX3VbWyJoZWFsdGh5Il1dKQoKIyBwbG90IFVOSVFVRSBQRVIgQ09ORFRJT04gTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X2NvbmRfbF91W1sxXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX3VbWzJdXSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbF91W1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X2NvbmRfbF91JGxlZywgCiAgICAgICAgICAgICAgICAgICBuY29sID0gNCwgcmVsX3dpZHRocyA9IGMoMSwxLDEsMC41KSkKCiMgZ2V0IG1lZGlhbiBwZXIgY2VsbCB0eXBlLCBwZXIgY29uZGl0aW9uIC0gSEVBTFRIWSBORVRXT1JLCnBlX2NvbmRfbF9oID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF9kZiwgZWRnZV9jb25kX2RmW2VkZ2VfY29uZF9kZiRpZF9jcF9pbnRlcmFjdGlvbiAlaW4lIGludGVyX2RmJGhlYWx0aHkkaWRfY3BfaW50ZXJhY3Rpb24sXSwgY2wgPSBjKCJjdCIsICJjdF9nMSIsICJjdF9nMiIpKQoKcGx0X2NvbmRfbF9oID0gbGlzdCgpCmZvcihjYyBpbiB1bmlxdWUocGVfY29uZF9sW1syXV0kY29uZGl0aW9uKSl7CiAgcGx0X2NvbmRfbF9oW1tjY11dID0gZ2dwbG90KCkrCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX2NvbmRfbF9oW1syXV1bcGVfY29uZF9sX2hbWzJdXSRjb25kaXRpb249PWNjLF0sIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLngsIHhlbmQgPSBYMS55LCB5ID0gWDIueCwgeWVuZCA9IFgyLnksIHNpemUgPSBGcmVxLCBhbHBoYSA9IEZyZXEpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHBlX2NvbmRfbF9oW1sxXV0sIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBmaWxsID0gY3QpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfY29uZF9sX2hbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF9oW1syXV0kRnJlcSkpKwogICAgbGFicyh0aXRsZSA9IGNjKSsKICAgIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpLAogICAgICAgICAgIGFscGhhID0gZ3VpZGVfbGVnZW5kKGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbnJvdyA9IDIpKSsKICAgIHRoZW1lX2NsYXNzaWMoKQp9CnBsdF9jb25kX2xfaFtbImxlZyJdXSA9IGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X2NvbmRfbF9oW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBIRUFMVEhZIE5FVFdPUksgbWVkaWFuIHBlciBjb25kaXRpb24KY293cGxvdDo6cGxvdF9ncmlkKHBsdF9jb25kX2xfaFtbMV1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbF9oW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF9jb25kX2xfaFtbM11dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIHBsdF9jb25kX2xfaCRsZWcsIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIHJlbF93aWR0aHMgPSBjKDEsMSwxLDAuNSkpCgojIGdldCBtZWRpYW4gcGVyIGNlbGwgdHlwZSwgcGVyIGNvbmRpdGlvbiAtIEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLCnBlX2NvbmRfbF9jaCA9IG1ha2VNZWRpYW5Db25kKHBvaW50X2NvbmRfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2VfY29uZF9kZltlZGdlX2NvbmRfZGYkaWRfY3BfaW50ZXJhY3Rpb24gJWluJSBjb21waF9pbnRlcnMsXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2wgPSBjKCJjdCIsICJjdF9nMSIsICJjdF9nMiIpKQoKcGx0X2NvbmRfbF9jaCA9IGxpc3QoKQpmb3IoY2MgaW4gdW5pcXVlKHBlX2NvbmRfbFtbMl1dJGNvbmRpdGlvbikpewogIHBsdF9jb25kX2xfY2hbW2NjXV0gPSBnZ3Bsb3QoKSsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGVfY29uZF9sX2NoW1syXV1bcGVfY29uZF9sX2NoW1syXV0kY29uZGl0aW9uPT1jYyxdLCAKICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV9jb25kX2xfY2hbWzFdXSwgCiAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDQpLCBsaW1pdHMgPSByYW5nZShwZV9jb25kX2xfY2hbWzJdXSRGcmVxKSkrCiAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKHBlX2NvbmRfbF9jaFtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfY29uZF9sX2NoW1sibGVnIl1dID0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfY29uZF9sX2NoW1siaGVhbHRoeSJdXSkKCiMgcGxvdCBIRUFMVEhZIENPTVBBUklTT04gTkVUV09SSyBtZWRpYW4gcGVyIGNvbmRpdGlvbgpjb3dwbG90OjpwbG90X2dyaWQocGx0X2NvbmRfbF9jaFtbMV1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgICAgICAgcGx0X2NvbmRfbF9jaFtbMl1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICAgICAgICBwbHRfY29uZF9sX2NoW1szXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgcGx0X2NvbmRfbF9jaCRsZWcsIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IDIsIHJlbF93aWR0aHMgPSBjKDEsMSwxLDAuNSkpCmBgYAoKU2F2ZSBuZXR3b3JrIG9iamVjdHMKCmBgYHtyfQpzYXZlKGVkZ2VfY29uZF9kZiwgcG9pbnRfY29uZF9kZiwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L25ldHdvcmtzX2NvbmQuUkRhdGEiKQpzYXZlKHBlX2wsIHBlX2NvbmRfbCwgcGVfY29uZF9sX3UsIHBlX2NvbmRfbF9oLCBwZV9jb25kX2xfY2gsIAogICAgIGZpbGUgPSAicmVzdWx0cy9jZWxsX2NvbW0vdXBkdC9tZWRpYW5fbmV0d29ya3NfY29uZC5SRGF0YSIpCmBgYAoKUGxvdCBsaWdhbmRzIGFuZCByZWNlcHRvcnMgd2l0aCBVTUFQCgpgYGB7cn0Kc2V0LnNlZWQoMjk1NCkKbCA9IHV3b3Q6OnVtYXAodChtZWFuX2V4cF9jb25kX2xyKSwgbWV0cmljID0gImNvc2luZSIsIHJldF9ubiA9IFQsIG5fZXBvY2hzID0gMTAwMCkKbF9jb25kID0gZGF0YS5mcmFtZShsJGVtYmVkZGluZykKbF9jb25kJGdlbmUgPSBjb2xuYW1lcyhtZWFuX2V4cF9jb25kX2xyKQpyb3duYW1lcyhsX2NvbmQpID0gY29sbmFtZXMobWVhbl9leHBfY29uZF9scikKdG1wX2RmID0gbWVyZ2UoZ2VuZV9wYWlyc19jb25kLCBsX2NvbmQsIGJ5LnggPSAiZ24xIiwgYnkueSA9ICJnZW5lIikKZWRnZV9jb25kX3VtYXBfZGYgPSBtZXJnZSh0bXBfZGYsIGxfY29uZCwgYnkueCA9ICJnbjIiLCBieS55ID0gImdlbmUiKQplZGdlX2NvbmRfdW1hcF9kZiA9IG1lcmdlKGVkZ2VfY29uZF91bWFwX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2N0KSwgYnkueCA9IDEsIGJ5LnkgPSAwLCBhbGwueCA9IFQpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY3QpLCBieS54ID0gMiwgYnkueSA9IDAsIGFsbC54ID0gVCkKY29sbmFtZXMoZWRnZV9jb25kX3VtYXBfZGYpWzk6MTBdID0gYygiY3RfZzEiLCAiY3RfZzIiKQplZGdlX2NvbmRfdW1hcF9kZiRtYWpfZzEgPSBpZmVsc2UoZ3JlcGwoIkxTRUMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSksICJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkhlcGF0b2N5dGVzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzEpLCAiSGVwYXRvY3l0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiU3RlbGxhdGUgY2VsbHMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSksICJNZXNlbmNoeW1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQ2hvbGFuZ2lvY3l0ZXMiLCBlZGdlX2NvbmRfdW1hcF9kZiRjdF9nMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hvbGFuZ2lvY3l0ZXMiLCAiSW1tdW5lIikpKSkKZWRnZV9jb25kX3VtYXBfZGYkbWFqX2cyID0gaWZlbHNlKGdyZXBsKCJMU0VDIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzIpLCAiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJIZXBhdG9jeXRlcyIsIGVkZ2VfY29uZF91bWFwX2RmJGN0X2cyKSwgIkhlcGF0b2N5dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlN0ZWxsYXRlIGNlbGxzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzIpLCAiTWVzZW5jaHltYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkNob2xhbmdpb2N5dGVzIiwgZWRnZV9jb25kX3VtYXBfZGYkY3RfZzIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNob2xhbmdpb2N5dGVzIiwgIkltbXVuZSIpKSkpCmVkZ2VfY29uZF91bWFwX2RmID0gbWVyZ2UoZWRnZV9jb25kX3VtYXBfZGYsIGRhdGEuZnJhbWUobWF4X2NvbmRfY3RfY29uZCksIGJ5LnggPSAxLCBieS55ID0gMCwgYWxsLnggPSBUKQplZGdlX2NvbmRfdW1hcF9kZiA9IG1lcmdlKGVkZ2VfY29uZF91bWFwX2RmLCBkYXRhLmZyYW1lKG1heF9jb25kX2N0X2NvbmQpLCBieS54ID0gMiwgYnkueSA9IDAsIGFsbC54ID0gVCkKY29sbmFtZXMoZWRnZV9jb25kX3VtYXBfZGYpWzRdID0gImNvbmQiCgpwb2ludF9jb25kX3VtYXBfZGYgPSBsX2NvbmQKcG9pbnRfY29uZF91bWFwX2RmJGN0ID0gbWF4X2NvbmRfY3RfY3RbcG9pbnRfY29uZF91bWFwX2RmJGdlbmVdCnBvaW50X2NvbmRfdW1hcF9kZiRjdDIgPSBpZmVsc2UoZ3JlcGwoIkxTRUMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpLCAiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJIZXBhdG9jeXRlcyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJIZXBhdG9jeXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJTdGVsbGF0ZSBjZWxscyIsIHBvaW50X2NvbmRfdW1hcF9kZiRjdCksICJNZXNlbmNoeW1hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQ2hvbGFuZ2lvY3l0ZXMiLCBwb2ludF9jb25kX3VtYXBfZGYkY3QpLCAiQ2hvbGFuZ2lvY3l0ZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJbW11bmUiKSkpKQpwb2ludF9jb25kX3VtYXBfZGYkY29uZCA9IG1heF9jb25kX2N0X2NvbmRbcG9pbnRfY29uZF91bWFwX2RmJGdlbmVdCgoKcGVfdW1hcF9sID0gbWFrZU1lZGlhbihwb2ludF9jb25kX3VtYXBfZGYsIGVkZ2VfY29uZF91bWFwX2RmLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgojIHBsb3QgdG90YWwgZ2VuZSBjb3JyZWxhdGlvbiBwcm9qZWN0aW9uIGFuZCBtZWRpYW4gbmV0d29yawpwbHRib3RoID0gZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChkYXRhID0gcG9pbnRfY29uZF91bWFwX2RmLCBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvdXIgPSBjdDIpLCAKICAgICAgICAgICAgIGFscGhhID0gMC4yNSwgc2hvdy5sZWdlbmQgPSBGKSsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygwLDQsMTkpKSsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX3VtYXBfbFtbMl1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEueCwgeGVuZCA9IFgxLnksIHkgPSBYMi54LCB5ZW5kID0gWDIueSwgc2l6ZSA9IEZyZXEpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjE1LCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fcG9pbnQoZGF0YSA9IHBlX3VtYXBfbFtbMV1dLCAKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGZpbGwgPSBjdCksIAogICAgICAgICAgICAgYWxwaGEgPSAxLCBwY2ggPSAyMSwgc2l6ZSA9IDQpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX2xbWzJdXSRGcmVxKSkrCiAgdGhlbWVfY2xhc3NpYygpKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpwcmludChwbHRib3RoKQoKcGx0Ym90aCA9IGdncGxvdCgpKwogIGdlb21fc2VnbWVudChkYXRhID0gZWRnZV9jb25kX3VtYXBfZGYsIAogICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMC4wMywgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludF9jb25kX3VtYXBfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG91ciA9IGN0MiksIAogICAgICAgICAgICAgYWxwaGEgPSAwLjYsIHNob3cubGVnZW5kID0gRikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCw0LDE5KSkrCiAgZ2VvbV9wb2ludChkYXRhID0gcGVfdW1hcF9sW1sxXV0sIAogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICBhbHBoYSA9IDEsIHBjaCA9IDIxLCBzaXplID0gNCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCA0KSwgbGltaXRzID0gcmFuZ2UocGVfbFtbMl1dJEZyZXEpKSsKICB0aGVtZV9jbGFzc2ljKCkrIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCnByaW50KHBsdGJvdGgpCgojIGdldCBtZWRpYW4gcGVyIGNlbGwgdHlwZSwgcGVyIGNvbmRpdGlvbiAtIEhFQUxUSFkgQ09NUEFSSVNPTiBORVRXT1JLCnBlX3VtYXBfY29uZF9sX2NoID0gbWFrZU1lZGlhbkNvbmQocG9pbnRfY29uZF91bWFwX2RmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2NvbmRfdW1hcF9kZltlZGdlX2NvbmRfdW1hcF9kZiRpZF9jcF9pbnRlcmFjdGlvbiVpbiVjb21waF9pbnRlcnMsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlX2J5ID0gImNvbmQiLCBjbCA9IGMoImN0IiwgImN0X2cxIiwgImN0X2cyIikpCgpwbHRfdW1hcF9jb25kX2xfY2ggPSBsaXN0KCkKZm9yKGNjIGluIHVuaXF1ZShwZV9jb25kX2xbWzJdXSRjb25kKSl7CiAgcGx0X3VtYXBfY29uZF9sX2NoW1tjY11dID0gZ2dwbG90KCkrCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBlX3VtYXBfY29uZF9sX2NoW1syXV1bcGVfdW1hcF9jb25kX2xfY2hbWzJdXSRjb25kPT1jYyxdLCAKICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBYMS54LCB4ZW5kID0gWDEueSwgeSA9IFgyLngsIHllbmQgPSBYMi55LCBzaXplID0gRnJlcSwgYWxwaGEgPSBGcmVxKSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwZV91bWFwX2NvbmRfbF9jaFtbMV1dLCAKICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgZmlsbCA9IGN0KSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgcGNoID0gMjEsIHNpemUgPSA0KSsKICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgNCksIGxpbWl0cyA9IHJhbmdlKHBlX3VtYXBfY29uZF9sX2NoW1syXV0kRnJlcSkpKwogICAgc2NhbGVfYWxwaGFfY29udGludW91cyhsaW1pdHMgPSByYW5nZShwZV91bWFwX2NvbmRfbF9jaFtbMl1dJEZyZXEpKSsKICAgIGxhYnModGl0bGUgPSBjYykrCiAgICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSwKICAgICAgICAgICBhbHBoYSA9IGd1aWRlX2xlZ2VuZChkaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIG5yb3cgPSAyKSkrCiAgICB0aGVtZV9jbGFzc2ljKCkKfQpwbHRfdW1hcF9jb25kX2xfY2hbWyJsZWciXV0gPSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF91bWFwX2NvbmRfbF9jaFtbImhlYWx0aHkiXV0pCgojIHBsb3QgSEVBTFRIWSBDT01QQVJJU09OIE5FVFdPUksgbWVkaWFuIHBlciBjb25kaXRpb24KY293cGxvdDo6cGxvdF9ncmlkKHBsdF91bWFwX2NvbmRfbF9jaFtbMV1dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgICAgICAgcGx0X3VtYXBfY29uZF9sX2NoW1syXV0rdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgIHBsdF91bWFwX2NvbmRfbF9jaFtbM11dK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIHBsdF91bWFwX2NvbmRfbF9jaCRsZWcsIAogICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIHJlbF93aWR0aHMgPSBjKDEsMSwxLDAuNSkpCmBgYAoKU2F2ZSBVTUFQIG5ldHdvcmsgb2JqZWN0cwoKYGBge3J9CnNhdmUoZWRnZV9jb25kX3VtYXBfZGYsIHBvaW50X2NvbmRfdW1hcF9kZiwgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L25ldHdvcmtzX2NvbmRfdW1hcC5SRGF0YSIpCnNhdmUocGVfdW1hcF9sLCBwZV91bWFwX2NvbmRfbF9jaCwgCiAgICAgZmlsZSA9ICJyZXN1bHRzL2NlbGxfY29tbS91cGR0L21lZGlhbl9uZXR3b3Jrc19jb25kX3VtYXAuUkRhdGEiKQpgYGAKCgoK